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/external.d.ts
CHANGED
|
@@ -262,6 +262,14 @@ export interface WriteablePayload {
|
|
|
262
262
|
id: string;
|
|
263
263
|
type: "issue" | "review";
|
|
264
264
|
} | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* pre-created `pullfrog` check-run, seeded `in_progress` by the server at dispatch so
|
|
267
|
+
* the PR's checks list shows the run before the GHA runner boots. the action PATCHes
|
|
268
|
+
* it to a terminal conclusion at run end. see `action/utils/runStatusCheck.ts`.
|
|
269
|
+
*/
|
|
270
|
+
checkRun?: {
|
|
271
|
+
id: string;
|
|
272
|
+
} | undefined;
|
|
265
273
|
/** when true, seed the PR summary tmpfile + persist edits at run end */
|
|
266
274
|
generateSummary?: boolean | undefined;
|
|
267
275
|
}
|
package/dist/index.js
CHANGED
|
@@ -100190,6 +100190,28 @@ var providers = {
|
|
|
100190
100190
|
}
|
|
100191
100191
|
}
|
|
100192
100192
|
}),
|
|
100193
|
+
"openai-compatible": provider({
|
|
100194
|
+
// "Custom" is the picker group, "OpenAI-compatible" the entry under it, so the
|
|
100195
|
+
// menu reads `Custom › OpenAI-compatible` and a second custom backend (a
|
|
100196
|
+
// different wire format, say) slots in beside it without a rename. the
|
|
100197
|
+
// provider KEY stays `openai-compatible` — it's the stored slug and the
|
|
100198
|
+
// `OPENAI_COMPATIBLE_*` env prefix, so this is display-only.
|
|
100199
|
+
displayName: "Custom",
|
|
100200
|
+
// bring-your-own generic OpenAI-compatible endpoint — Cloudflare AI Gateway,
|
|
100201
|
+
// Alibaba DashScope, self-hosted vLLM, or any compatible gateway. base URL +
|
|
100202
|
+
// key + model ID are all supplied via env; nothing is cataloged or bumped.
|
|
100203
|
+
envVars: ["OPENAI_COMPATIBLE_BASE_URL", "OPENAI_COMPATIBLE_API_KEY", "OPENAI_COMPATIBLE_MODEL"],
|
|
100204
|
+
models: {
|
|
100205
|
+
// single routing entry — the actual model ID is read from
|
|
100206
|
+
// OPENAI_COMPATIBLE_MODEL at run time and the provider is materialized
|
|
100207
|
+
// via `@ai-sdk/openai-compatible`.
|
|
100208
|
+
byok: {
|
|
100209
|
+
displayName: "OpenAI-compatible",
|
|
100210
|
+
resolve: "openai-compatible",
|
|
100211
|
+
routing: "openai-compatible"
|
|
100212
|
+
}
|
|
100213
|
+
}
|
|
100214
|
+
}),
|
|
100193
100215
|
openrouter: provider({
|
|
100194
100216
|
displayName: "OpenRouter",
|
|
100195
100217
|
envVars: ["OPENROUTER_API_KEY"],
|
|
@@ -100407,6 +100429,12 @@ var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
|
|
|
100407
100429
|
var defaultProxyDisplayName = defaultProxyAlias.displayName;
|
|
100408
100430
|
var BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
|
|
100409
100431
|
var VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
|
|
100432
|
+
var OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
|
|
100433
|
+
var OPENAI_COMPATIBLE_BASE_URL_ENV = "OPENAI_COMPATIBLE_BASE_URL";
|
|
100434
|
+
var OPENAI_COMPATIBLE_API_KEY_ENV = "OPENAI_COMPATIBLE_API_KEY";
|
|
100435
|
+
var OPENAI_COMPATIBLE_MODEL_ENV = "OPENAI_COMPATIBLE_MODEL";
|
|
100436
|
+
var OPENAI_COMPATIBLE_CONTEXT_ENV = "OPENAI_COMPATIBLE_CONTEXT";
|
|
100437
|
+
var OPENAI_COMPATIBLE_MAX_OUTPUT_ENV = "OPENAI_COMPATIBLE_MAX_OUTPUT";
|
|
100410
100438
|
function isBedrockAnthropicId(bedrockModelId) {
|
|
100411
100439
|
return bedrockModelId.toLowerCase().split(/[./:]/).includes("anthropic");
|
|
100412
100440
|
}
|
|
@@ -101093,7 +101121,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
101093
101121
|
// package.json
|
|
101094
101122
|
var package_default = {
|
|
101095
101123
|
name: "pullfrog",
|
|
101096
|
-
version: "0.1.
|
|
101124
|
+
version: "0.1.47",
|
|
101097
101125
|
type: "module",
|
|
101098
101126
|
bin: {
|
|
101099
101127
|
pullfrog: "dist/cli.mjs",
|
|
@@ -108882,8 +108910,9 @@ function readModels(cliPath) {
|
|
|
108882
108910
|
stdio: ["ignore", "pipe", "pipe"]
|
|
108883
108911
|
});
|
|
108884
108912
|
if (result.status !== 0) {
|
|
108885
|
-
|
|
108886
|
-
|
|
108913
|
+
const stderr = (result.stderr ?? "").replace(ANSI_PATTERN, "").trim();
|
|
108914
|
+
failure = stderr || result.error?.message;
|
|
108915
|
+
log.debug(`\xBB \`opencode models\` failed (${result.status}): ${failure}`);
|
|
108887
108916
|
return /* @__PURE__ */ new Set();
|
|
108888
108917
|
}
|
|
108889
108918
|
failure = void 0;
|
|
@@ -108941,6 +108970,29 @@ function geminiHighThinkingOverrides() {
|
|
|
108941
108970
|
])
|
|
108942
108971
|
);
|
|
108943
108972
|
}
|
|
108973
|
+
function openAICompatibleLimit() {
|
|
108974
|
+
return {
|
|
108975
|
+
context: Number(process.env[OPENAI_COMPATIBLE_CONTEXT_ENV]),
|
|
108976
|
+
output: Number(process.env[OPENAI_COMPATIBLE_MAX_OUTPUT_ENV])
|
|
108977
|
+
};
|
|
108978
|
+
}
|
|
108979
|
+
function openAICompatibleProvider(model) {
|
|
108980
|
+
const prefix = `${OPENAI_COMPATIBLE_PROVIDER}/`;
|
|
108981
|
+
if (!model?.startsWith(prefix)) return {};
|
|
108982
|
+
const modelId = model.slice(prefix.length);
|
|
108983
|
+
return {
|
|
108984
|
+
[OPENAI_COMPATIBLE_PROVIDER]: {
|
|
108985
|
+
npm: "@ai-sdk/openai-compatible",
|
|
108986
|
+
name: "OpenAI-compatible",
|
|
108987
|
+
options: {
|
|
108988
|
+
// trailing slash would double up against opencode's `/chat/completions` join
|
|
108989
|
+
baseURL: process.env[OPENAI_COMPATIBLE_BASE_URL_ENV]?.replace(/\/$/, ""),
|
|
108990
|
+
apiKey: process.env[OPENAI_COMPATIBLE_API_KEY_ENV]
|
|
108991
|
+
},
|
|
108992
|
+
models: { [modelId]: { name: modelId, limit: openAICompatibleLimit() } }
|
|
108993
|
+
}
|
|
108994
|
+
};
|
|
108995
|
+
}
|
|
108944
108996
|
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
108945
108997
|
function kimiOpenRouterProviderOverrides() {
|
|
108946
108998
|
return Object.fromEntries(
|
|
@@ -109032,7 +109084,8 @@ function buildSecurityConfig(ctx, model) {
|
|
|
109032
109084
|
google: { models: geminiHighThinkingOverrides() },
|
|
109033
109085
|
openrouter: {
|
|
109034
109086
|
models: { ...deepseekHighEffortOverrides(), ...kimiOpenRouterProviderOverrides() }
|
|
109035
|
-
}
|
|
109087
|
+
},
|
|
109088
|
+
...openAICompatibleProvider(model)
|
|
109036
109089
|
}
|
|
109037
109090
|
};
|
|
109038
109091
|
if (model) {
|
|
@@ -158027,6 +158080,9 @@ var JsonPayload = type({
|
|
|
158027
158080
|
id: "string",
|
|
158028
158081
|
type: "'issue' | 'review'"
|
|
158029
158082
|
}).or("undefined"),
|
|
158083
|
+
// optional so a payload from an older server build (pre-`checkRun`) still parses
|
|
158084
|
+
// against a newer action across a rolling deploy.
|
|
158085
|
+
"checkRun?": type({ id: "string" }).or("undefined"),
|
|
158030
158086
|
"generateSummary?": "boolean | undefined"
|
|
158031
158087
|
});
|
|
158032
158088
|
var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
|
|
@@ -158140,13 +158196,19 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
|
158140
158196
|
timeout: inputs.timeout ?? jsonPayload?.timeout,
|
|
158141
158197
|
cwd: resolveCwd(inputs.cwd),
|
|
158142
158198
|
progressComment: jsonPayload?.progressComment,
|
|
158199
|
+
checkRun: jsonPayload?.checkRun,
|
|
158143
158200
|
generateSummary: jsonPayload?.generateSummary,
|
|
158144
158201
|
// permissions: inputs > repoSettings > fallbacks
|
|
158145
158202
|
push: inputs.push ?? repoSettings.push ?? "restricted",
|
|
158146
158203
|
shell: resolvedShell,
|
|
158147
|
-
//
|
|
158148
|
-
//
|
|
158149
|
-
|
|
158204
|
+
// the `pullfrog` run-lifecycle check. ON by default — the whole point is that a PR
|
|
158205
|
+
// shows whether Pullfrog is running without anyone having to opt in. the workflow
|
|
158206
|
+
// input is the source of truth when set (mirrors `push`); otherwise the repo
|
|
158207
|
+
// setting decides.
|
|
158208
|
+
runStatusCheck: inputs.status_checks === void 0 ? repoSettings.statusChecks : inputs.status_checks === "enabled",
|
|
158209
|
+
// the `pullfrog-approval` verdict check stays opt-in and workflow-only. it exists to
|
|
158210
|
+
// be *required* by branch protection, so it must never turn itself on.
|
|
158211
|
+
approvalCheck: inputs.status_checks === "enabled",
|
|
158150
158212
|
// temporary progress chrome. the workflow input is the source of truth when
|
|
158151
158213
|
// set (mirrors `push`); otherwise the repo setting decides. defaults to true.
|
|
158152
158214
|
progressComments: inputs.progress_comments === void 0 ? repoSettings.progressComments : inputs.progress_comments === "enabled",
|
|
@@ -158674,7 +158736,11 @@ async function approveAfterFix(ctx) {
|
|
|
158674
158736
|
approved: true,
|
|
158675
158737
|
hasComments: false
|
|
158676
158738
|
});
|
|
158677
|
-
ctx.toolState.approval = {
|
|
158739
|
+
ctx.toolState.approval = {
|
|
158740
|
+
wouldApprove: true,
|
|
158741
|
+
sha: headSha,
|
|
158742
|
+
url: result.data.html_url
|
|
158743
|
+
};
|
|
158678
158744
|
log.info(`\xBB auto-approved #${pullNumber} after fix run (review ${result.data.id})`);
|
|
158679
158745
|
await deleteProgressComment(ctx).catch((err) => {
|
|
158680
158746
|
log.debug(`progress comment cleanup after fix auto-approval failed: ${err}`);
|
|
@@ -159007,6 +159073,7 @@ function CreatePullRequestReviewTool(ctx) {
|
|
|
159007
159073
|
}
|
|
159008
159074
|
const reviewId = result.data.id;
|
|
159009
159075
|
const reviewNodeId = result.data.node_id;
|
|
159076
|
+
if (ctx.toolState.approval) ctx.toolState.approval.url = result.data.html_url;
|
|
159010
159077
|
log.info(`\xBB created review ${reviewId} on pull request #${pull_number}`);
|
|
159011
159078
|
const actuallyReviewedSha = primary.checkoutSha ?? params.commit_id;
|
|
159012
159079
|
ctx.toolState.review = {
|
|
@@ -163298,6 +163365,15 @@ function resolveSlug(slug2) {
|
|
|
163298
163365
|
}
|
|
163299
163366
|
return vertexId;
|
|
163300
163367
|
}
|
|
163368
|
+
if (alias?.routing === "openai-compatible") {
|
|
163369
|
+
const modelId = process.env[OPENAI_COMPATIBLE_MODEL_ENV]?.trim();
|
|
163370
|
+
if (!modelId) {
|
|
163371
|
+
throw new Error(
|
|
163372
|
+
`${OPENAI_COMPATIBLE_MODEL_ENV} env var is required when the model is set to "${slug2}". set it to the model ID served by your OpenAI-compatible endpoint (e.g. a Cloudflare AI Gateway or DashScope model). see https://docs.pullfrog.com/openai-compatible for setup.`
|
|
163373
|
+
);
|
|
163374
|
+
}
|
|
163375
|
+
return `${OPENAI_COMPATIBLE_PROVIDER}/${modelId}`;
|
|
163376
|
+
}
|
|
163301
163377
|
return resolveCliModel(slug2);
|
|
163302
163378
|
}
|
|
163303
163379
|
function resolveModel(ctx) {
|
|
@@ -163347,6 +163423,21 @@ function resolveAgent(ctx) {
|
|
|
163347
163423
|
|
|
163348
163424
|
// utils/apiKeys.ts
|
|
163349
163425
|
var MISSING_KEY_MARKER = "no API key found";
|
|
163426
|
+
var SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
|
|
163427
|
+
function buildKeyError(params) {
|
|
163428
|
+
return params.secretsUnavailable ? buildSecretsUnavailableError(params) : buildMissingApiKeyError(params);
|
|
163429
|
+
}
|
|
163430
|
+
function buildSecretsUnavailableError(params) {
|
|
163431
|
+
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
163432
|
+
const modelClause = params.model ? ` needed for \`${params.model}\`` : "";
|
|
163433
|
+
return [
|
|
163434
|
+
`**Pullfrog ${SECRETS_UNAVAILABLE_MARKER}${modelClause} on this run.** The key is still stored \u2014 the runner just couldn't fetch it, so this is a transient failure on our side, not a missing key.`,
|
|
163435
|
+
"",
|
|
163436
|
+
"**To fix:** re-run the job. If it keeps happening, tell us in Discord.",
|
|
163437
|
+
"",
|
|
163438
|
+
`[Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
163439
|
+
].join("\n");
|
|
163440
|
+
}
|
|
163350
163441
|
function buildMissingApiKeyError(params) {
|
|
163351
163442
|
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
163352
163443
|
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
@@ -163389,10 +163480,47 @@ add the missing secret(s) to your GitHub repository at ${githubSecretsUrl}, then
|
|
|
163389
163480
|
|
|
163390
163481
|
for full setup instructions, see https://docs.pullfrog.com/vertex`;
|
|
163391
163482
|
}
|
|
163483
|
+
function buildOpenAICompatibleSetupError(params) {
|
|
163484
|
+
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
163485
|
+
return `OpenAI-compatible model selected but required configuration is missing: ${params.missing.join(", ")}.
|
|
163486
|
+
|
|
163487
|
+
only the API key is sensitive \u2014 add it as a secret at ${githubSecretsUrl}. everything else is plain workflow \`env:\`:
|
|
163488
|
+
|
|
163489
|
+
${OPENAI_COMPATIBLE_BASE_URL_ENV}: https://your-endpoint.example.com/v1
|
|
163490
|
+
${OPENAI_COMPATIBLE_API_KEY_ENV}: \${{ secrets.${OPENAI_COMPATIBLE_API_KEY_ENV} }}
|
|
163491
|
+
${OPENAI_COMPATIBLE_MODEL_ENV}: <model-id>
|
|
163492
|
+
${OPENAI_COMPATIBLE_CONTEXT_ENV}: "128000"
|
|
163493
|
+
${OPENAI_COMPATIBLE_MAX_OUTPUT_ENV}: "16384"
|
|
163494
|
+
|
|
163495
|
+
set the last two to the real limits of the model your endpoint serves. Pullfrog can't
|
|
163496
|
+
discover them \u2014 your endpoint owns the model catalog \u2014 and without them completions are
|
|
163497
|
+
capped at 32000 tokens (rejected outright by models with a smaller cap) and
|
|
163498
|
+
auto-compaction is disabled, so long runs grow until your endpoint refuses them.
|
|
163499
|
+
|
|
163500
|
+
for full setup instructions, see https://docs.pullfrog.com/openai-compatible`;
|
|
163501
|
+
}
|
|
163392
163502
|
function hasEnvVar3(name) {
|
|
163393
163503
|
const value2 = process.env[name];
|
|
163394
163504
|
return typeof value2 === "string" && value2.length > 0;
|
|
163395
163505
|
}
|
|
163506
|
+
function hasPositiveNumberEnvVar(name) {
|
|
163507
|
+
return Number(process.env[name]) > 0;
|
|
163508
|
+
}
|
|
163509
|
+
function validateOpenAICompatibleSetup(params) {
|
|
163510
|
+
const missing = [];
|
|
163511
|
+
if (!hasEnvVar3(OPENAI_COMPATIBLE_BASE_URL_ENV)) missing.push(OPENAI_COMPATIBLE_BASE_URL_ENV);
|
|
163512
|
+
if (!hasEnvVar3(OPENAI_COMPATIBLE_API_KEY_ENV)) missing.push(OPENAI_COMPATIBLE_API_KEY_ENV);
|
|
163513
|
+
if (!hasEnvVar3(OPENAI_COMPATIBLE_MODEL_ENV)) missing.push(OPENAI_COMPATIBLE_MODEL_ENV);
|
|
163514
|
+
if (!hasPositiveNumberEnvVar(OPENAI_COMPATIBLE_CONTEXT_ENV))
|
|
163515
|
+
missing.push(OPENAI_COMPATIBLE_CONTEXT_ENV);
|
|
163516
|
+
if (!hasPositiveNumberEnvVar(OPENAI_COMPATIBLE_MAX_OUTPUT_ENV))
|
|
163517
|
+
missing.push(OPENAI_COMPATIBLE_MAX_OUTPUT_ENV);
|
|
163518
|
+
if (missing.length > 0) {
|
|
163519
|
+
throw new Error(
|
|
163520
|
+
buildOpenAICompatibleSetupError({ owner: params.owner, name: params.name, missing })
|
|
163521
|
+
);
|
|
163522
|
+
}
|
|
163523
|
+
}
|
|
163396
163524
|
function validateBedrockSetup(params) {
|
|
163397
163525
|
const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
|
|
163398
163526
|
const missing = [];
|
|
@@ -163427,6 +163555,10 @@ function validateAgentApiKey(params) {
|
|
|
163427
163555
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
163428
163556
|
return;
|
|
163429
163557
|
}
|
|
163558
|
+
if (params.model.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`)) {
|
|
163559
|
+
validateOpenAICompatibleSetup({ owner: params.owner, name: params.name });
|
|
163560
|
+
return;
|
|
163561
|
+
}
|
|
163430
163562
|
if (!params.model.includes("/")) {
|
|
163431
163563
|
if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
|
|
163432
163564
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
@@ -163439,23 +163571,46 @@ function validateAgentApiKey(params) {
|
|
|
163439
163571
|
if (params.authorized.has(params.model)) return;
|
|
163440
163572
|
const reason = getModelsFailure();
|
|
163441
163573
|
if (reason) throw new Error(reason);
|
|
163574
|
+
if (getModelEnvVars(params.model).some(hasEnvVar3)) return;
|
|
163442
163575
|
throw new Error(
|
|
163443
|
-
|
|
163576
|
+
buildKeyError({
|
|
163577
|
+
owner: params.owner,
|
|
163578
|
+
name: params.name,
|
|
163579
|
+
model: params.model,
|
|
163580
|
+
secretsUnavailable: params.secretsUnavailable
|
|
163581
|
+
})
|
|
163444
163582
|
);
|
|
163445
163583
|
}
|
|
163446
163584
|
if (hasEnvVar3("ANTHROPIC_API_KEY") || hasEnvVar3("CLAUDE_CODE_OAUTH_TOKEN")) return;
|
|
163447
163585
|
throw new Error(
|
|
163448
|
-
|
|
163586
|
+
buildKeyError({
|
|
163587
|
+
owner: params.owner,
|
|
163588
|
+
name: params.name,
|
|
163589
|
+
model: params.model,
|
|
163590
|
+
secretsUnavailable: params.secretsUnavailable
|
|
163591
|
+
})
|
|
163449
163592
|
);
|
|
163450
163593
|
}
|
|
163451
163594
|
if (params.agent.name === "opencode") {
|
|
163452
163595
|
if (params.authorized.size > 0) return;
|
|
163453
163596
|
const reason = getModelsFailure();
|
|
163454
163597
|
if (reason) throw new Error(reason);
|
|
163455
|
-
throw new Error(
|
|
163598
|
+
throw new Error(
|
|
163599
|
+
buildKeyError({
|
|
163600
|
+
owner: params.owner,
|
|
163601
|
+
name: params.name,
|
|
163602
|
+
secretsUnavailable: params.secretsUnavailable
|
|
163603
|
+
})
|
|
163604
|
+
);
|
|
163456
163605
|
}
|
|
163457
163606
|
if (hasEnvVar3("ANTHROPIC_API_KEY") || hasEnvVar3("CLAUDE_CODE_OAUTH_TOKEN")) return;
|
|
163458
|
-
throw new Error(
|
|
163607
|
+
throw new Error(
|
|
163608
|
+
buildKeyError({
|
|
163609
|
+
owner: params.owner,
|
|
163610
|
+
name: params.name,
|
|
163611
|
+
secretsUnavailable: params.secretsUnavailable
|
|
163612
|
+
})
|
|
163613
|
+
);
|
|
163459
163614
|
}
|
|
163460
163615
|
function isApiKeyAuthError(text) {
|
|
163461
163616
|
if (!text) return false;
|
|
@@ -164140,7 +164295,7 @@ async function persistXrepoLearnings(ctx) {
|
|
|
164140
164295
|
// utils/modelAccess.ts
|
|
164141
164296
|
function decideModelAccess(input) {
|
|
164142
164297
|
if (!input.modelExplicit || !input.model) return { kind: "ok" };
|
|
164143
|
-
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/"));
|
|
164298
|
+
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`));
|
|
164144
164299
|
if (input.proxyActive) {
|
|
164145
164300
|
const target = resolveOpenRouterModel(input.model);
|
|
164146
164301
|
if (input.oss) {
|
|
@@ -164935,6 +165090,7 @@ var defaultSettings = {
|
|
|
164935
165090
|
autoMergeEnabled: false,
|
|
164936
165091
|
signedCommits: false,
|
|
164937
165092
|
progressComments: true,
|
|
165093
|
+
statusChecks: true,
|
|
164938
165094
|
modeInstructions: {},
|
|
164939
165095
|
learnings: null,
|
|
164940
165096
|
learningsHeadings: [],
|
|
@@ -164949,6 +165105,10 @@ var defaultRunContext = {
|
|
|
164949
165105
|
oss: false,
|
|
164950
165106
|
plan: "none"
|
|
164951
165107
|
};
|
|
165108
|
+
var unknownSecretsRunContext = {
|
|
165109
|
+
...defaultRunContext,
|
|
165110
|
+
secretsUnavailable: true
|
|
165111
|
+
};
|
|
164952
165112
|
async function fetchRunContext(params) {
|
|
164953
165113
|
const timeoutMs = 3e4;
|
|
164954
165114
|
const controller = new AbortController();
|
|
@@ -164967,7 +165127,7 @@ async function fetchRunContext(params) {
|
|
|
164967
165127
|
});
|
|
164968
165128
|
clearTimeout(timeoutId);
|
|
164969
165129
|
if (!response.ok) {
|
|
164970
|
-
return defaultRunContext;
|
|
165130
|
+
return response.status >= 500 ? unknownSecretsRunContext : defaultRunContext;
|
|
164971
165131
|
}
|
|
164972
165132
|
const data = await response.json();
|
|
164973
165133
|
if (data === null) {
|
|
@@ -164991,11 +165151,12 @@ async function fetchRunContext(params) {
|
|
|
164991
165151
|
oss: data.oss ?? false,
|
|
164992
165152
|
plan: data.plan ?? "none",
|
|
164993
165153
|
proxyModel: data.proxyModel,
|
|
164994
|
-
dbSecrets: data.dbSecrets
|
|
165154
|
+
dbSecrets: data.dbSecrets,
|
|
165155
|
+
secretsUnavailable: data.secretsUnavailable
|
|
164995
165156
|
};
|
|
164996
165157
|
} catch {
|
|
164997
165158
|
clearTimeout(timeoutId);
|
|
164998
|
-
return
|
|
165159
|
+
return unknownSecretsRunContext;
|
|
164999
165160
|
}
|
|
165000
165161
|
}
|
|
165001
165162
|
|
|
@@ -165017,7 +165178,10 @@ async function resolveRunContextData(params) {
|
|
|
165017
165178
|
const repoContext = parseRepoContext();
|
|
165018
165179
|
let oidcToken;
|
|
165019
165180
|
try {
|
|
165020
|
-
oidcToken = await core9.getIDToken("pullfrog-api")
|
|
165181
|
+
oidcToken = await op(() => core9.getIDToken("pullfrog-api"), {
|
|
165182
|
+
name: "OIDC mint",
|
|
165183
|
+
retries: process.env.ACTIONS_ID_TOKEN_REQUEST_URL ? [200, 1e3] : []
|
|
165184
|
+
})();
|
|
165021
165185
|
} catch {
|
|
165022
165186
|
}
|
|
165023
165187
|
const [repoResponse, runContext] = await Promise.all([
|
|
@@ -165039,7 +165203,10 @@ async function resolveRunContextData(params) {
|
|
|
165039
165203
|
oss: runContext.oss,
|
|
165040
165204
|
plan: runContext.plan,
|
|
165041
165205
|
proxyModel: runContext.proxyModel,
|
|
165042
|
-
dbSecrets: runContext.dbSecrets
|
|
165206
|
+
dbSecrets: runContext.dbSecrets,
|
|
165207
|
+
// a failed mint on a runner that should have been able to mint is the same
|
|
165208
|
+
// outcome as the server-side failure: the run never sees stored secrets.
|
|
165209
|
+
secretsUnavailable: runContext.secretsUnavailable || !!process.env.ACTIONS_ID_TOKEN_REQUEST_URL && oidcToken === void 0
|
|
165043
165210
|
};
|
|
165044
165211
|
}
|
|
165045
165212
|
|
|
@@ -165204,6 +165371,9 @@ function renderRunError(input) {
|
|
|
165204
165371
|
if (input.errorMessage.includes(MODEL_ACCESS_MARKER)) {
|
|
165205
165372
|
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
165206
165373
|
}
|
|
165374
|
+
if (input.errorMessage.includes(SECRETS_UNAVAILABLE_MARKER)) {
|
|
165375
|
+
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
165376
|
+
}
|
|
165207
165377
|
const isHang = input.errorMessage.startsWith("activity timeout") || input.errorMessage.startsWith("agent still pending");
|
|
165208
165378
|
const hangBody = isHang ? formatAgentHangBody({
|
|
165209
165379
|
diagnostic: input.agentDiagnostic,
|
|
@@ -165442,30 +165612,123 @@ function getCurrentWorkflowFilename() {
|
|
|
165442
165612
|
return match3?.[1] ?? "pullfrog.yml";
|
|
165443
165613
|
}
|
|
165444
165614
|
|
|
165445
|
-
// utils/
|
|
165446
|
-
var
|
|
165447
|
-
var
|
|
165448
|
-
|
|
165615
|
+
// utils/runStatusCheck.ts
|
|
165616
|
+
var RUN_STATUS_CHECK_NAME = "Pullfrog";
|
|
165617
|
+
var APPROVAL_CHECK_NAME = "Pullfrog approval";
|
|
165618
|
+
function parseCheckRunId(raw2) {
|
|
165619
|
+
if (!raw2?.id) return void 0;
|
|
165620
|
+
const id = parseInt(raw2.id, 10);
|
|
165621
|
+
if (Number.isNaN(id) || id <= 0) return void 0;
|
|
165622
|
+
return id;
|
|
165623
|
+
}
|
|
165624
|
+
function disableCheckLine(owner, repo) {
|
|
165625
|
+
const url4 = `https://pullfrog.com/console/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}#auto-review-prs`;
|
|
165626
|
+
return `
|
|
165627
|
+
|
|
165628
|
+
[Turn off this check \u2192](${url4}) \u2014 it reports run status only and gates nothing unless you required it in branch protection.`;
|
|
165629
|
+
}
|
|
165630
|
+
var TERMINAL_OUTPUT = {
|
|
165631
|
+
success: {
|
|
165632
|
+
title: "Pullfrog run completed",
|
|
165633
|
+
summary: "The Pullfrog run finished successfully."
|
|
165634
|
+
},
|
|
165635
|
+
failure: {
|
|
165636
|
+
title: "Pullfrog run failed",
|
|
165637
|
+
summary: "The Pullfrog run failed. See the run logs for details."
|
|
165638
|
+
},
|
|
165639
|
+
cancelled: {
|
|
165640
|
+
title: "Pullfrog run cancelled",
|
|
165641
|
+
summary: "The Pullfrog run was cancelled before it finished."
|
|
165642
|
+
},
|
|
165643
|
+
timed_out: {
|
|
165644
|
+
title: "Pullfrog run timed out",
|
|
165645
|
+
summary: "The Pullfrog run exceeded its timeout. See the run logs for details."
|
|
165646
|
+
},
|
|
165647
|
+
action_required: {
|
|
165648
|
+
title: "Pullfrog run needs attention",
|
|
165649
|
+
summary: "The Pullfrog run stopped and needs attention. See the run logs for details."
|
|
165650
|
+
},
|
|
165651
|
+
neutral: {
|
|
165652
|
+
title: "Pullfrog run finished",
|
|
165653
|
+
summary: "The Pullfrog run finished without a pass or fail outcome."
|
|
165654
|
+
},
|
|
165655
|
+
skipped: {
|
|
165656
|
+
title: "Pullfrog run skipped",
|
|
165657
|
+
summary: "This run was superseded by another Pullfrog run."
|
|
165658
|
+
},
|
|
165659
|
+
stale: {
|
|
165660
|
+
title: "Pullfrog run didn't finish",
|
|
165661
|
+
summary: "Pullfrog never received a completion signal for this run. See the run logs for details."
|
|
165662
|
+
}
|
|
165663
|
+
};
|
|
165664
|
+
function terminalOutput(params) {
|
|
165665
|
+
const base = TERMINAL_OUTPUT[params.conclusion];
|
|
165666
|
+
const review = params.reviewUrl ? `
|
|
165667
|
+
|
|
165668
|
+
[View the review Pullfrog posted \u2192](${params.reviewUrl})` : "";
|
|
165669
|
+
const disable = params.conclusion === "success" ? "" : disableCheckLine(params.owner, params.repo);
|
|
165670
|
+
return { title: base.title, summary: base.summary + review + disable };
|
|
165671
|
+
}
|
|
165672
|
+
async function finalizeRunStatusCheck(params) {
|
|
165673
|
+
const updateParams = {
|
|
165674
|
+
owner: params.owner,
|
|
165675
|
+
repo: params.repo,
|
|
165676
|
+
check_run_id: params.checkRunId,
|
|
165677
|
+
status: "completed",
|
|
165678
|
+
conclusion: params.conclusion,
|
|
165679
|
+
output: terminalOutput({
|
|
165680
|
+
conclusion: params.conclusion,
|
|
165681
|
+
owner: params.owner,
|
|
165682
|
+
repo: params.repo,
|
|
165683
|
+
reviewUrl: params.reviewUrl
|
|
165684
|
+
})
|
|
165685
|
+
};
|
|
165686
|
+
if (params.detailsUrl) updateParams.details_url = params.detailsUrl;
|
|
165687
|
+
await params.octokit.rest.checks.update(updateParams);
|
|
165688
|
+
}
|
|
165689
|
+
async function createTerminalRunStatusCheck(params) {
|
|
165449
165690
|
const createParams = {
|
|
165450
|
-
owner:
|
|
165451
|
-
repo:
|
|
165452
|
-
name:
|
|
165691
|
+
owner: params.owner,
|
|
165692
|
+
repo: params.repo,
|
|
165693
|
+
name: RUN_STATUS_CHECK_NAME,
|
|
165453
165694
|
head_sha: params.headSha,
|
|
165454
165695
|
status: "completed",
|
|
165455
165696
|
conclusion: params.conclusion,
|
|
165456
|
-
output: {
|
|
165697
|
+
output: terminalOutput({
|
|
165698
|
+
conclusion: params.conclusion,
|
|
165699
|
+
owner: params.owner,
|
|
165700
|
+
repo: params.repo,
|
|
165701
|
+
reviewUrl: params.reviewUrl
|
|
165702
|
+
})
|
|
165457
165703
|
};
|
|
165458
|
-
if (
|
|
165459
|
-
|
|
165460
|
-
}
|
|
165461
|
-
await ctx.octokit.rest.checks.create(createParams);
|
|
165462
|
-
log.info(`\xBB posted ${params.name} check (${params.conclusion}) on ${params.headSha.slice(0, 7)}`);
|
|
165704
|
+
if (params.detailsUrl) createParams.details_url = params.detailsUrl;
|
|
165705
|
+
await params.octokit.rest.checks.create(createParams);
|
|
165463
165706
|
}
|
|
165707
|
+
|
|
165708
|
+
// utils/statusChecks.ts
|
|
165464
165709
|
async function reportStatusChecks(ctx, params) {
|
|
165465
|
-
if (!ctx.payload.statusChecks) return;
|
|
165466
165710
|
const event = ctx.payload.event;
|
|
165467
165711
|
const pullNumber = event.issue_number;
|
|
165468
165712
|
if (event.is_pr !== true || typeof pullNumber !== "number") return;
|
|
165713
|
+
const checkRunId = parseCheckRunId(ctx.payload.checkRun);
|
|
165714
|
+
if (checkRunId === void 0 && !ctx.payload.runStatusCheck && !ctx.payload.approvalCheck) return;
|
|
165715
|
+
const conclusion = params.runSucceeded ? "success" : "failure";
|
|
165716
|
+
const detailsUrl = ctx.runId ? `https://github.com/${ctx.repo.owner}/${ctx.repo.name}/actions/runs/${ctx.runId}` : void 0;
|
|
165717
|
+
if (checkRunId !== void 0) {
|
|
165718
|
+
await finalizeRunStatusCheck({
|
|
165719
|
+
octokit: ctx.octokit,
|
|
165720
|
+
owner: ctx.repo.owner,
|
|
165721
|
+
repo: ctx.repo.name,
|
|
165722
|
+
checkRunId,
|
|
165723
|
+
conclusion,
|
|
165724
|
+
detailsUrl,
|
|
165725
|
+
reviewUrl: ctx.toolState.approval?.url
|
|
165726
|
+
}).then(() => log.info(`\xBB finalized ${RUN_STATUS_CHECK_NAME} check (${conclusion})`)).catch((err) => log.debug(`status checks: ${RUN_STATUS_CHECK_NAME} finalize failed: ${err}`));
|
|
165727
|
+
}
|
|
165728
|
+
const approval = ctx.toolState.approval;
|
|
165729
|
+
const needsApprovalCheck = ctx.payload.approvalCheck && params.runSucceeded && approval;
|
|
165730
|
+
const needsFallbackRunCheck = ctx.payload.runStatusCheck && checkRunId === void 0;
|
|
165731
|
+
if (!needsApprovalCheck && !needsFallbackRunCheck) return;
|
|
165469
165732
|
let headSha;
|
|
165470
165733
|
try {
|
|
165471
165734
|
const pr = await ctx.octokit.rest.pulls.get({
|
|
@@ -165478,24 +165741,32 @@ async function reportStatusChecks(ctx, params) {
|
|
|
165478
165741
|
log.debug(`status checks: failed to resolve PR #${pullNumber} head sha: ${err}`);
|
|
165479
165742
|
return;
|
|
165480
165743
|
}
|
|
165481
|
-
|
|
165482
|
-
|
|
165483
|
-
|
|
165484
|
-
|
|
165485
|
-
|
|
165486
|
-
|
|
165487
|
-
|
|
165488
|
-
|
|
165489
|
-
|
|
165490
|
-
|
|
165491
|
-
|
|
165492
|
-
|
|
165493
|
-
|
|
165494
|
-
|
|
165744
|
+
if (needsFallbackRunCheck) {
|
|
165745
|
+
await createTerminalRunStatusCheck({
|
|
165746
|
+
octokit: ctx.octokit,
|
|
165747
|
+
owner: ctx.repo.owner,
|
|
165748
|
+
repo: ctx.repo.name,
|
|
165749
|
+
headSha: primaryRepoState(ctx.toolState).checkoutSha ?? headSha,
|
|
165750
|
+
conclusion,
|
|
165751
|
+
detailsUrl,
|
|
165752
|
+
reviewUrl: ctx.toolState.approval?.url
|
|
165753
|
+
}).then(() => log.info(`\xBB posted ${RUN_STATUS_CHECK_NAME} check (${conclusion})`)).catch((err) => log.debug(`status checks: ${RUN_STATUS_CHECK_NAME} post failed: ${err}`));
|
|
165754
|
+
}
|
|
165755
|
+
if (!needsApprovalCheck || !approval) return;
|
|
165756
|
+
const createParams = {
|
|
165757
|
+
owner: ctx.repo.owner,
|
|
165758
|
+
repo: ctx.repo.name,
|
|
165759
|
+
name: APPROVAL_CHECK_NAME,
|
|
165760
|
+
head_sha: approval.sha ?? headSha,
|
|
165761
|
+
status: "completed",
|
|
165762
|
+
conclusion: approval.wouldApprove ? "success" : "failure",
|
|
165763
|
+
output: {
|
|
165495
165764
|
title: approval.wouldApprove ? "Pullfrog would approve" : "Pullfrog would not approve",
|
|
165496
165765
|
summary: approval.wouldApprove ? "Pullfrog has no outstanding review feedback on this PR." : "Pullfrog has outstanding review feedback or requested changes on this PR."
|
|
165497
|
-
}
|
|
165498
|
-
}
|
|
165766
|
+
}
|
|
165767
|
+
};
|
|
165768
|
+
if (detailsUrl) createParams.details_url = detailsUrl;
|
|
165769
|
+
await ctx.octokit.rest.checks.create(createParams).then(() => log.info(`\xBB posted ${APPROVAL_CHECK_NAME} check`)).catch((err) => log.debug(`status checks: ${APPROVAL_CHECK_NAME} post failed: ${err}`));
|
|
165499
165770
|
}
|
|
165500
165771
|
|
|
165501
165772
|
// utils/runLifecycle.ts
|
|
@@ -165938,7 +166209,8 @@ async function main() {
|
|
|
165938
166209
|
model: effectiveModel,
|
|
165939
166210
|
authorized: getAuthorizedModels(),
|
|
165940
166211
|
owner: runContext.repo.owner,
|
|
165941
|
-
name: runContext.repo.name
|
|
166212
|
+
name: runContext.repo.name,
|
|
166213
|
+
secretsUnavailable: runContext.secretsUnavailable
|
|
165942
166214
|
});
|
|
165943
166215
|
}
|
|
165944
166216
|
await setupGit({
|
package/dist/internal/index.d.ts
CHANGED
|
@@ -15,4 +15,6 @@ export { isLeapingIntoActionCommentBody, LEAPING_INTO_ACTION_PREFIX, } from "../
|
|
|
15
15
|
export { MAX_LEARNINGS_LENGTH, truncateAtLineBoundary } from "../utils/learningsTruncate.ts";
|
|
16
16
|
export type { CreateProgressCommentTarget, ProgressComment, ProgressCommentType, } from "../utils/progressComment.ts";
|
|
17
17
|
export { createLeapingProgressComment, deleteProgressCommentApi, getProgressComment, updateProgressComment, } from "../utils/progressComment.ts";
|
|
18
|
+
export type { RunStatusCheckConclusion, RunStatusCheckOctokit, } from "../utils/runStatusCheck.ts";
|
|
19
|
+
export { APPROVAL_CHECK_NAME, createRunStatusCheck, finalizeRunStatusCheck, RUN_STATUS_CHECK_NAME, runStatusCheckNeedsFinalizing, } from "../utils/runStatusCheck.ts";
|
|
18
20
|
export { isValidTimeString, parseTimeString, TIMEOUT_DISABLED, } from "../utils/time.ts";
|