pullfrog 0.1.45 → 0.1.47
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/README.md +1 -1
- package/dist/agents/opencodeShared.d.ts +25 -0
- package/dist/cli.mjs +337 -58
- package/dist/external.d.ts +8 -0
- package/dist/index.js +321 -49
- package/dist/internal/index.d.ts +2 -0
- package/dist/internal.js +134 -0
- package/dist/models.d.ts +27 -1
- package/dist/toolState.d.ts +1 -0
- package/dist/utils/apiKeys.d.ts +10 -0
- package/dist/utils/payload.d.ts +8 -1
- package/dist/utils/runContext.d.ts +8 -0
- package/dist/utils/runContextData.d.ts +3 -0
- package/dist/utils/runStatusCheck.d.ts +170 -0
- package/dist/utils/statusChecks.d.ts +14 -17
- package/package.json +1 -1
package/dist/internal.js
CHANGED
|
@@ -381,6 +381,28 @@ var providers = {
|
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
383
|
}),
|
|
384
|
+
"openai-compatible": provider({
|
|
385
|
+
// "Custom" is the picker group, "OpenAI-compatible" the entry under it, so the
|
|
386
|
+
// menu reads `Custom › OpenAI-compatible` and a second custom backend (a
|
|
387
|
+
// different wire format, say) slots in beside it without a rename. the
|
|
388
|
+
// provider KEY stays `openai-compatible` — it's the stored slug and the
|
|
389
|
+
// `OPENAI_COMPATIBLE_*` env prefix, so this is display-only.
|
|
390
|
+
displayName: "Custom",
|
|
391
|
+
// bring-your-own generic OpenAI-compatible endpoint — Cloudflare AI Gateway,
|
|
392
|
+
// Alibaba DashScope, self-hosted vLLM, or any compatible gateway. base URL +
|
|
393
|
+
// key + model ID are all supplied via env; nothing is cataloged or bumped.
|
|
394
|
+
envVars: ["OPENAI_COMPATIBLE_BASE_URL", "OPENAI_COMPATIBLE_API_KEY", "OPENAI_COMPATIBLE_MODEL"],
|
|
395
|
+
models: {
|
|
396
|
+
// single routing entry — the actual model ID is read from
|
|
397
|
+
// OPENAI_COMPATIBLE_MODEL at run time and the provider is materialized
|
|
398
|
+
// via `@ai-sdk/openai-compatible`.
|
|
399
|
+
byok: {
|
|
400
|
+
displayName: "OpenAI-compatible",
|
|
401
|
+
resolve: "openai-compatible",
|
|
402
|
+
routing: "openai-compatible"
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}),
|
|
384
406
|
openrouter: provider({
|
|
385
407
|
displayName: "OpenRouter",
|
|
386
408
|
envVars: ["OPENROUTER_API_KEY"],
|
|
@@ -1307,6 +1329,113 @@ async function createLeapingProgressComment(ctx, target, body) {
|
|
|
1307
1329
|
};
|
|
1308
1330
|
}
|
|
1309
1331
|
|
|
1332
|
+
// utils/runStatusCheck.ts
|
|
1333
|
+
var RUN_STATUS_CHECK_NAME = "Pullfrog";
|
|
1334
|
+
var APPROVAL_CHECK_NAME = "Pullfrog approval";
|
|
1335
|
+
var IN_PROGRESS_OUTPUT = {
|
|
1336
|
+
title: "Pullfrog is running",
|
|
1337
|
+
summary: "Pullfrog is working on this pull request. This check updates when the run finishes."
|
|
1338
|
+
};
|
|
1339
|
+
function disableCheckLine(owner, repo) {
|
|
1340
|
+
const url = `https://pullfrog.com/console/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}#auto-review-prs`;
|
|
1341
|
+
return `
|
|
1342
|
+
|
|
1343
|
+
[Turn off this check \u2192](${url}) \u2014 it reports run status only and gates nothing unless you required it in branch protection.`;
|
|
1344
|
+
}
|
|
1345
|
+
var TERMINAL_OUTPUT = {
|
|
1346
|
+
success: {
|
|
1347
|
+
title: "Pullfrog run completed",
|
|
1348
|
+
summary: "The Pullfrog run finished successfully."
|
|
1349
|
+
},
|
|
1350
|
+
failure: {
|
|
1351
|
+
title: "Pullfrog run failed",
|
|
1352
|
+
summary: "The Pullfrog run failed. See the run logs for details."
|
|
1353
|
+
},
|
|
1354
|
+
cancelled: {
|
|
1355
|
+
title: "Pullfrog run cancelled",
|
|
1356
|
+
summary: "The Pullfrog run was cancelled before it finished."
|
|
1357
|
+
},
|
|
1358
|
+
timed_out: {
|
|
1359
|
+
title: "Pullfrog run timed out",
|
|
1360
|
+
summary: "The Pullfrog run exceeded its timeout. See the run logs for details."
|
|
1361
|
+
},
|
|
1362
|
+
action_required: {
|
|
1363
|
+
title: "Pullfrog run needs attention",
|
|
1364
|
+
summary: "The Pullfrog run stopped and needs attention. See the run logs for details."
|
|
1365
|
+
},
|
|
1366
|
+
neutral: {
|
|
1367
|
+
title: "Pullfrog run finished",
|
|
1368
|
+
summary: "The Pullfrog run finished without a pass or fail outcome."
|
|
1369
|
+
},
|
|
1370
|
+
skipped: {
|
|
1371
|
+
title: "Pullfrog run skipped",
|
|
1372
|
+
summary: "This run was superseded by another Pullfrog run."
|
|
1373
|
+
},
|
|
1374
|
+
stale: {
|
|
1375
|
+
title: "Pullfrog run didn't finish",
|
|
1376
|
+
summary: "Pullfrog never received a completion signal for this run. See the run logs for details."
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
function terminalOutput(params) {
|
|
1380
|
+
const base = TERMINAL_OUTPUT[params.conclusion];
|
|
1381
|
+
const review = params.reviewUrl ? `
|
|
1382
|
+
|
|
1383
|
+
[View the review Pullfrog posted \u2192](${params.reviewUrl})` : "";
|
|
1384
|
+
const disable = params.conclusion === "success" ? "" : disableCheckLine(params.owner, params.repo);
|
|
1385
|
+
return { title: base.title, summary: base.summary + review + disable };
|
|
1386
|
+
}
|
|
1387
|
+
async function createRunStatusCheck(params) {
|
|
1388
|
+
const existing = await params.octokit.rest.checks.listForRef({
|
|
1389
|
+
owner: params.owner,
|
|
1390
|
+
repo: params.repo,
|
|
1391
|
+
ref: params.headSha,
|
|
1392
|
+
check_name: RUN_STATUS_CHECK_NAME,
|
|
1393
|
+
status: "in_progress"
|
|
1394
|
+
}).catch(() => void 0);
|
|
1395
|
+
const reusable = existing?.data.check_runs[0];
|
|
1396
|
+
if (reusable) return reusable.id;
|
|
1397
|
+
const createParams = {
|
|
1398
|
+
owner: params.owner,
|
|
1399
|
+
repo: params.repo,
|
|
1400
|
+
name: RUN_STATUS_CHECK_NAME,
|
|
1401
|
+
head_sha: params.headSha,
|
|
1402
|
+
status: "in_progress",
|
|
1403
|
+
output: IN_PROGRESS_OUTPUT
|
|
1404
|
+
};
|
|
1405
|
+
if (params.detailsUrl) createParams.details_url = params.detailsUrl;
|
|
1406
|
+
const created = await params.octokit.rest.checks.create(createParams);
|
|
1407
|
+
return created.data.id;
|
|
1408
|
+
}
|
|
1409
|
+
async function finalizeRunStatusCheck(params) {
|
|
1410
|
+
const updateParams = {
|
|
1411
|
+
owner: params.owner,
|
|
1412
|
+
repo: params.repo,
|
|
1413
|
+
check_run_id: params.checkRunId,
|
|
1414
|
+
status: "completed",
|
|
1415
|
+
conclusion: params.conclusion,
|
|
1416
|
+
output: terminalOutput({
|
|
1417
|
+
conclusion: params.conclusion,
|
|
1418
|
+
owner: params.owner,
|
|
1419
|
+
repo: params.repo,
|
|
1420
|
+
reviewUrl: params.reviewUrl
|
|
1421
|
+
})
|
|
1422
|
+
};
|
|
1423
|
+
if (params.detailsUrl) updateParams.details_url = params.detailsUrl;
|
|
1424
|
+
await params.octokit.rest.checks.update(updateParams);
|
|
1425
|
+
}
|
|
1426
|
+
async function runStatusCheckNeedsFinalizing(params) {
|
|
1427
|
+
try {
|
|
1428
|
+
const existing = await params.octokit.rest.checks.get({
|
|
1429
|
+
owner: params.owner,
|
|
1430
|
+
repo: params.repo,
|
|
1431
|
+
check_run_id: params.checkRunId
|
|
1432
|
+
});
|
|
1433
|
+
return existing.data.status !== "completed";
|
|
1434
|
+
} catch {
|
|
1435
|
+
return false;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1310
1439
|
// utils/time.ts
|
|
1311
1440
|
var TIMEOUT_DISABLED = "none";
|
|
1312
1441
|
var TIME_STRING_REGEX = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
|
|
@@ -1322,6 +1451,7 @@ function isValidTimeString(input) {
|
|
|
1322
1451
|
return parseTimeString(input) !== null;
|
|
1323
1452
|
}
|
|
1324
1453
|
export {
|
|
1454
|
+
APPROVAL_CHECK_NAME,
|
|
1325
1455
|
AUTO_EFFICIENT,
|
|
1326
1456
|
AUTO_INTELLIGENT,
|
|
1327
1457
|
DEFAULT_PROXY_MODEL,
|
|
@@ -1329,12 +1459,15 @@ export {
|
|
|
1329
1459
|
MAX_LEARNINGS_LENGTH,
|
|
1330
1460
|
OAuthInvalidGrantError,
|
|
1331
1461
|
PULLFROG_DIVIDER,
|
|
1462
|
+
RUN_STATUS_CHECK_NAME,
|
|
1332
1463
|
TIMEOUT_DISABLED,
|
|
1333
1464
|
buildPullfrogFooter,
|
|
1334
1465
|
createLeapingProgressComment,
|
|
1466
|
+
createRunStatusCheck,
|
|
1335
1467
|
decodeJwtExpMs,
|
|
1336
1468
|
defaultAutoTier,
|
|
1337
1469
|
deleteProgressCommentApi,
|
|
1470
|
+
finalizeRunStatusCheck,
|
|
1338
1471
|
getAutoSelectHintModel,
|
|
1339
1472
|
getModelEnvVars,
|
|
1340
1473
|
getModelManagedCredentials,
|
|
@@ -1358,6 +1491,7 @@ export {
|
|
|
1358
1491
|
resolveDisplayAlias,
|
|
1359
1492
|
resolveModelSlug,
|
|
1360
1493
|
resolveOpenRouterModel,
|
|
1494
|
+
runStatusCheckNeedsFinalizing,
|
|
1361
1495
|
stringifyCodexAuthBody,
|
|
1362
1496
|
stripExistingFooter,
|
|
1363
1497
|
truncateAtLineBoundary,
|
package/dist/models.d.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* env var and routes to claude-code for Anthropic IDs or opencode for
|
|
21
21
|
* everything else.
|
|
22
22
|
*/
|
|
23
|
-
export type ModelRouting = "bedrock" | "vertex";
|
|
23
|
+
export type ModelRouting = "bedrock" | "vertex" | "openai-compatible";
|
|
24
24
|
export interface ModelAlias {
|
|
25
25
|
/** stable alias stored in DB, e.g. "anthropic/claude-opus" */
|
|
26
26
|
slug: string;
|
|
@@ -99,6 +99,7 @@ export declare const providers: {
|
|
|
99
99
|
"opencode-go": ProviderConfig;
|
|
100
100
|
bedrock: ProviderConfig;
|
|
101
101
|
vertex: ProviderConfig;
|
|
102
|
+
"openai-compatible": ProviderConfig;
|
|
102
103
|
openrouter: ProviderConfig;
|
|
103
104
|
};
|
|
104
105
|
export type ModelProvider = keyof typeof providers;
|
|
@@ -196,6 +197,31 @@ export declare function getAutoSelectHintModel(): string;
|
|
|
196
197
|
export declare const BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
|
|
197
198
|
/** env var that supplies the Vertex AI model ID for the `vertex/byok` slug. */
|
|
198
199
|
export declare const VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
|
|
200
|
+
/** provider key + slug prefix for the generic OpenAI-compatible BYOK backend. */
|
|
201
|
+
export declare const OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
|
|
202
|
+
/** base URL of the user's OpenAI-compatible endpoint (e.g. a Cloudflare AI Gateway URL). */
|
|
203
|
+
export declare const OPENAI_COMPATIBLE_BASE_URL_ENV = "OPENAI_COMPATIBLE_BASE_URL";
|
|
204
|
+
/** API key/token for the user's OpenAI-compatible endpoint — the one sensitive secret. */
|
|
205
|
+
export declare const OPENAI_COMPATIBLE_API_KEY_ENV = "OPENAI_COMPATIBLE_API_KEY";
|
|
206
|
+
/** model ID served by the endpoint, supplied for the `openai-compatible/byok` slug. */
|
|
207
|
+
export declare const OPENAI_COMPATIBLE_MODEL_ENV = "OPENAI_COMPATIBLE_MODEL";
|
|
208
|
+
/**
|
|
209
|
+
* context-window size of the endpoint's model. required — `validateOpenAICompatibleSetup`
|
|
210
|
+
* rejects the run pre-agent when it's unset or non-numeric. it also gates
|
|
211
|
+
* auto-compaction: opencode's `isOverflow` short-circuits when
|
|
212
|
+
* `limit.context === 0`, which would otherwise let a long session grow until the
|
|
213
|
+
* endpoint rejects it on context length.
|
|
214
|
+
*/
|
|
215
|
+
export declare const OPENAI_COMPATIBLE_CONTEXT_ENV = "OPENAI_COMPATIBLE_CONTEXT";
|
|
216
|
+
/**
|
|
217
|
+
* max completion tokens the endpoint's model accepts. required — see
|
|
218
|
+
* OPENAI_COMPATIBLE_CONTEXT_ENV. opencode has no models.dev metadata for a
|
|
219
|
+
* user-supplied endpoint, and an undeclared limit makes it send
|
|
220
|
+
* `max_tokens: 32000`, which most models reject outright (gpt-4o and gpt-4o-mini
|
|
221
|
+
* cap at 16384, many open models at 4096/8192). opencode's `limit` requires
|
|
222
|
+
* `context` + `output` together, so the pair is validated and emitted as a unit.
|
|
223
|
+
*/
|
|
224
|
+
export declare const OPENAI_COMPATIBLE_MAX_OUTPUT_ENV = "OPENAI_COMPATIBLE_MAX_OUTPUT";
|
|
199
225
|
/**
|
|
200
226
|
* the Bedrock model ID passed to claude-code or opencode is whatever the
|
|
201
227
|
* user set in `BEDROCK_MODEL_ID` — Pullfrog never resolves or upgrades it.
|
package/dist/toolState.d.ts
CHANGED
package/dist/utils/apiKeys.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* marker for the distinct "run-context couldn't hand over your stored secrets"
|
|
3
|
+
* body. surfaced verbatim by `runErrorRenderer` (same contract as
|
|
4
|
+
* `MODEL_ACCESS_MARKER`) because blaming the user for a transient fetch failure
|
|
5
|
+
* on our side is the wrong CTA — their key is configured and still stored.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
|
|
1
8
|
/**
|
|
2
9
|
* Validate that the resolved model can actually be served by the chosen
|
|
3
10
|
* agent. For routing slugs (Bedrock / Vertex) the auth shape is multi-var
|
|
@@ -16,6 +23,9 @@ export declare function validateAgentApiKey(params: {
|
|
|
16
23
|
authorized: Set<string>;
|
|
17
24
|
owner: string;
|
|
18
25
|
name: string;
|
|
26
|
+
/** run-context couldn't hand over Pullfrog-stored secrets, so a missing key
|
|
27
|
+
* says nothing about what the user actually configured. */
|
|
28
|
+
secretsUnavailable?: boolean | undefined;
|
|
19
29
|
}): void;
|
|
20
30
|
/**
|
|
21
31
|
* Detect agent-runtime auth failures that should be reformatted as an actionable
|
package/dist/utils/payload.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
|
|
|
22
22
|
id: string;
|
|
23
23
|
type: "issue" | "review";
|
|
24
24
|
} | undefined;
|
|
25
|
+
checkRun?: {
|
|
26
|
+
id: string;
|
|
27
|
+
} | undefined;
|
|
25
28
|
generateSummary?: boolean | undefined;
|
|
26
29
|
}, {}>;
|
|
27
30
|
export declare const Inputs: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
@@ -68,10 +71,14 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
|
|
|
68
71
|
id: string;
|
|
69
72
|
type: "issue" | "review";
|
|
70
73
|
} | undefined;
|
|
74
|
+
checkRun: {
|
|
75
|
+
id: string;
|
|
76
|
+
} | undefined;
|
|
71
77
|
generateSummary: boolean | undefined;
|
|
72
78
|
push: import("../external.ts").PushPermission;
|
|
73
79
|
shell: import("../external.ts").ShellPermission;
|
|
74
|
-
|
|
80
|
+
runStatusCheck: boolean;
|
|
81
|
+
approvalCheck: boolean;
|
|
75
82
|
progressComments: boolean;
|
|
76
83
|
proxyModel: string | undefined;
|
|
77
84
|
};
|
|
@@ -34,6 +34,7 @@ export interface RepoSettings {
|
|
|
34
34
|
autoMergeEnabled: boolean;
|
|
35
35
|
signedCommits: boolean;
|
|
36
36
|
progressComments: boolean;
|
|
37
|
+
statusChecks: boolean;
|
|
37
38
|
modeInstructions: Record<string, string>;
|
|
38
39
|
learnings: string | null;
|
|
39
40
|
learningsHeadings: LearningsHeading[];
|
|
@@ -55,6 +56,13 @@ export interface RunContext {
|
|
|
55
56
|
plan: AccountPlan;
|
|
56
57
|
proxyModel?: string | undefined;
|
|
57
58
|
dbSecrets?: Record<string, string> | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* the server tried and failed to materialize Pullfrog-stored secrets (or we
|
|
61
|
+
* never got a usable response at all). distinct from an absent `dbSecrets`,
|
|
62
|
+
* which legitimately means the user has none stored — without the
|
|
63
|
+
* distinction a transient failure renders as "you have no API key".
|
|
64
|
+
*/
|
|
65
|
+
secretsUnavailable?: boolean | undefined;
|
|
58
66
|
}
|
|
59
67
|
/**
|
|
60
68
|
* fetch run context from Pullfrog API
|
|
@@ -13,6 +13,9 @@ export interface RunContextData {
|
|
|
13
13
|
plan: AccountPlan;
|
|
14
14
|
proxyModel?: string | undefined;
|
|
15
15
|
dbSecrets?: Record<string, string> | undefined;
|
|
16
|
+
/** stored secrets couldn't be materialized for this run — not the same as
|
|
17
|
+
* the user having none. see `RunContext.secretsUnavailable`. */
|
|
18
|
+
secretsUnavailable?: boolean | undefined;
|
|
16
19
|
}
|
|
17
20
|
interface ResolveRunContextDataParams {
|
|
18
21
|
octokit: OctokitWithPlugins;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the `pullfrog` commit-status check-run — the row a PR's
|
|
3
|
+
* checks list shows while a run is in flight and after it ends.
|
|
4
|
+
*
|
|
5
|
+
* Pullfrog dispatches its workflow with `ref: <default branch>`, so the GitHub Actions
|
|
6
|
+
* run attaches its check suite to the default branch head and never to the PR head.
|
|
7
|
+
* Nothing about a run is therefore visible from the PR itself unless we post this
|
|
8
|
+
* check-run against the PR head sha ourselves.
|
|
9
|
+
*
|
|
10
|
+
* Created `in_progress` by the server at dispatch — before the runner boots, and
|
|
11
|
+
* upstream of every action-side failure that would otherwise leave the PR silent
|
|
12
|
+
* (billing 402, token resolution, bad model slug, missing provider key). Moved to a
|
|
13
|
+
* terminal conclusion by whichever of three actors reaches it first, all calling
|
|
14
|
+
* `finalizeRunStatusCheck`:
|
|
15
|
+
*
|
|
16
|
+
* 1. the action at run end (`reportStatusChecks`)
|
|
17
|
+
* 2. the `workflow_run.completed` webhook (covers cancel / SIGKILL / early throw)
|
|
18
|
+
* 3. the hourly stuck-run reaper (covers a dropped webhook)
|
|
19
|
+
*
|
|
20
|
+
* That three-layer close-out is what makes an `in_progress` check safe here: a check
|
|
21
|
+
* stranded `in_progress` on a required branch-protection rule would block merges
|
|
22
|
+
* forever, which is why this file did not exist before.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* the run-lifecycle check. verdict-agnostic — it reports that a run happened, not what it
|
|
26
|
+
* found. this string IS the branch-protection identifier, so changing it silently breaks
|
|
27
|
+
* any repo that required the old name.
|
|
28
|
+
*/
|
|
29
|
+
export declare const RUN_STATUS_CHECK_NAME = "Pullfrog";
|
|
30
|
+
/** the review-verdict check. opt-in, terminal-only, and deliberately separate from the above. */
|
|
31
|
+
export declare const APPROVAL_CHECK_NAME = "Pullfrog approval";
|
|
32
|
+
/**
|
|
33
|
+
* The terminal states we report. Exactly GitHub's check-run `conclusion` enum, which
|
|
34
|
+
* happens to be `WorkflowRunStatus` minus `running` — so the server can map a finished
|
|
35
|
+
* run's status straight across (`checkConclusionFromStatus` in utils/workflowRunStatus.ts).
|
|
36
|
+
*/
|
|
37
|
+
export type RunStatusCheckConclusion = "success" | "failure" | "cancelled" | "timed_out" | "action_required" | "neutral" | "skipped" | "stale";
|
|
38
|
+
/**
|
|
39
|
+
* Parse the on-the-wire `{ id: string }` shape (the form carried in `JsonPayload`) into
|
|
40
|
+
* a check-run id. Mirrors `parseProgressComment` — returns undefined when the id isn't a
|
|
41
|
+
* positive integer so callers can short-circuit cleanly.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseCheckRunId(raw: {
|
|
44
|
+
id: string;
|
|
45
|
+
} | null | undefined): number | undefined;
|
|
46
|
+
interface CheckRunResponse {
|
|
47
|
+
data: {
|
|
48
|
+
id: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
type CheckRunStatus = "queued" | "in_progress" | "completed";
|
|
52
|
+
type CheckRunOutput = {
|
|
53
|
+
title: string;
|
|
54
|
+
summary: string;
|
|
55
|
+
};
|
|
56
|
+
type CreateCheckRunParams = {
|
|
57
|
+
owner: string;
|
|
58
|
+
repo: string;
|
|
59
|
+
name: string;
|
|
60
|
+
head_sha: string;
|
|
61
|
+
status?: CheckRunStatus;
|
|
62
|
+
conclusion?: RunStatusCheckConclusion;
|
|
63
|
+
details_url?: string;
|
|
64
|
+
output?: CheckRunOutput;
|
|
65
|
+
};
|
|
66
|
+
type UpdateCheckRunParams = {
|
|
67
|
+
owner: string;
|
|
68
|
+
repo: string;
|
|
69
|
+
check_run_id: number;
|
|
70
|
+
status?: CheckRunStatus;
|
|
71
|
+
conclusion?: RunStatusCheckConclusion;
|
|
72
|
+
details_url?: string;
|
|
73
|
+
output?: CheckRunOutput;
|
|
74
|
+
};
|
|
75
|
+
export interface RunStatusCheckOctokit {
|
|
76
|
+
rest: {
|
|
77
|
+
checks: {
|
|
78
|
+
create: (params: CreateCheckRunParams) => Promise<CheckRunResponse>;
|
|
79
|
+
update: (params: UpdateCheckRunParams) => Promise<CheckRunResponse>;
|
|
80
|
+
get: (params: {
|
|
81
|
+
owner: string;
|
|
82
|
+
repo: string;
|
|
83
|
+
check_run_id: number;
|
|
84
|
+
}) => Promise<{
|
|
85
|
+
data: {
|
|
86
|
+
status: string;
|
|
87
|
+
};
|
|
88
|
+
}>;
|
|
89
|
+
listForRef: (params: {
|
|
90
|
+
owner: string;
|
|
91
|
+
repo: string;
|
|
92
|
+
ref: string;
|
|
93
|
+
check_name?: string;
|
|
94
|
+
status?: "queued" | "in_progress" | "completed";
|
|
95
|
+
}) => Promise<{
|
|
96
|
+
data: {
|
|
97
|
+
check_runs: {
|
|
98
|
+
id: number;
|
|
99
|
+
}[];
|
|
100
|
+
};
|
|
101
|
+
}>;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Post the `in_progress` check-run on `headSha` and return its id for the finalizers.
|
|
107
|
+
*
|
|
108
|
+
* Best-effort: a transient 5xx, a repo that revoked `checks: write`, or a PR whose head
|
|
109
|
+
* has already been deleted must never stop a run from dispatching. Returns undefined on
|
|
110
|
+
* failure, which every caller treats as "no check to finalize".
|
|
111
|
+
*/
|
|
112
|
+
export declare function createRunStatusCheck(params: {
|
|
113
|
+
octokit: RunStatusCheckOctokit;
|
|
114
|
+
owner: string;
|
|
115
|
+
repo: string;
|
|
116
|
+
headSha: string;
|
|
117
|
+
detailsUrl: string | undefined;
|
|
118
|
+
}): Promise<number | undefined>;
|
|
119
|
+
/**
|
|
120
|
+
* Move an existing check-run to its terminal conclusion.
|
|
121
|
+
*
|
|
122
|
+
* A PATCH, not a second create: `POST /check-runs` with the same name + sha creates a
|
|
123
|
+
* SECOND row rather than replacing the first, so creating twice would show the PR two
|
|
124
|
+
* contradictory `pullfrog` rows. (That is a real bug today — `finalizeSuccessRun` posts
|
|
125
|
+
* success and a later throw posts failure, both as fresh rows.)
|
|
126
|
+
*/
|
|
127
|
+
export declare function finalizeRunStatusCheck(params: {
|
|
128
|
+
octokit: RunStatusCheckOctokit;
|
|
129
|
+
owner: string;
|
|
130
|
+
repo: string;
|
|
131
|
+
checkRunId: number;
|
|
132
|
+
conclusion: RunStatusCheckConclusion;
|
|
133
|
+
detailsUrl: string | undefined;
|
|
134
|
+
reviewUrl?: string | undefined;
|
|
135
|
+
}): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Post an already-terminal check-run in one call.
|
|
138
|
+
*
|
|
139
|
+
* The fallback for runs that never got a server-created check to update: a rolling
|
|
140
|
+
* deploy where the dispatch payload predates `checkRun`, or a workflow invoked outside
|
|
141
|
+
* Pullfrog's own dispatch path (`prompt_file`, a hand-written step).
|
|
142
|
+
*/
|
|
143
|
+
export declare function createTerminalRunStatusCheck(params: {
|
|
144
|
+
octokit: RunStatusCheckOctokit;
|
|
145
|
+
owner: string;
|
|
146
|
+
repo: string;
|
|
147
|
+
headSha: string;
|
|
148
|
+
conclusion: RunStatusCheckConclusion;
|
|
149
|
+
detailsUrl: string | undefined;
|
|
150
|
+
reviewUrl?: string | undefined;
|
|
151
|
+
}): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Whether this check still needs finalizing.
|
|
154
|
+
*
|
|
155
|
+
* The server-side close-outs are BACKSTOPS: they exist for runs the action could not
|
|
156
|
+
* finalize itself. Firing them unconditionally is actively harmful, because the action
|
|
157
|
+
* writes a richer summary than they can — it knows the review URL, they do not — so a
|
|
158
|
+
* blind re-PATCH silently strips that link seconds after it appears. It also resets
|
|
159
|
+
* `completed_at`, discarding the duration GitHub renders as "Successful in 2m".
|
|
160
|
+
*
|
|
161
|
+
* Returns false when GitHub cannot be reached: a backstop that cannot confirm the check
|
|
162
|
+
* is unfinished must not overwrite a good one.
|
|
163
|
+
*/
|
|
164
|
+
export declare function runStatusCheckNeedsFinalizing(params: {
|
|
165
|
+
octokit: RunStatusCheckOctokit;
|
|
166
|
+
owner: string;
|
|
167
|
+
repo: string;
|
|
168
|
+
checkRunId: number;
|
|
169
|
+
}): Promise<boolean>;
|
|
170
|
+
export {};
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import type { ToolContext } from "../mcp/server.ts";
|
|
2
2
|
/**
|
|
3
|
-
* post the
|
|
4
|
-
*
|
|
5
|
-
* protection. no-op unless the `status_checks` input is enabled and the run is
|
|
6
|
-
* on a pull request.
|
|
3
|
+
* post the `Pullfrog` (run lifecycle) and `Pullfrog approval` (review verdict)
|
|
4
|
+
* commit-status check-runs.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* - `Pullfrog` is on by default (`Repo.statusChecks`). the server already created it
|
|
7
|
+
* `in_progress` at dispatch, so the work here is a PATCH to its terminal conclusion —
|
|
8
|
+
* see `runStatusCheck.ts` for why a second create would leave two contradictory rows.
|
|
9
|
+
* the terminal-create fallback covers a payload with no `checkRun` (older server
|
|
10
|
+
* build mid-rolling-deploy, or a workflow driven outside Pullfrog's dispatch path).
|
|
11
|
+
* - `Pullfrog approval` stays opt-in (`status_checks: enabled`) and terminal-only:
|
|
12
|
+
* it asserts a review verdict, which only exists once a run produces one. anchored
|
|
13
|
+
* to the exact reviewed sha so a mid-run push leaves the new head unapproved until
|
|
14
|
+
* a follow-up re-review reports.
|
|
12
15
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* verdict (`toolState.approval`, set by create_pull_request_review),
|
|
17
|
-
* anchored to the reviewed sha so a mid-run push leaves the new head
|
|
18
|
-
* unapproved until the follow-up re-review reports.
|
|
19
|
-
*
|
|
20
|
-
* best-effort throughout: a check-post failure (fork PR head not in the base
|
|
21
|
-
* repo, transient 5xx, closed PR) must never flip the run's own outcome.
|
|
16
|
+
* best-effort throughout: a check-post failure (transient 5xx, closed PR, revoked
|
|
17
|
+
* permission) must never flip the run's own outcome. the `workflow_run.completed` webhook
|
|
18
|
+
* and both stuck-run reaper sweeps close out a check this function fails to finalize.
|
|
22
19
|
*/
|
|
23
20
|
export declare function reportStatusChecks(ctx: ToolContext, params: {
|
|
24
21
|
runSucceeded: boolean;
|