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/cli.mjs
CHANGED
|
@@ -101356,6 +101356,10 @@ async function setPullfrogSecret(ctx) {
|
|
|
101356
101356
|
}
|
|
101357
101357
|
return { saved: false, error: result.data.error || `api returned ${result.status}` };
|
|
101358
101358
|
}
|
|
101359
|
+
function describeSecretTarget(ctx) {
|
|
101360
|
+
if (ctx.scope === "account") return `account ${import_picocolors.default.cyan(`@${ctx.owner}`)}`;
|
|
101361
|
+
return `repo ${import_picocolors.default.cyan(`${ctx.owner}/${ctx.repo}`)}`;
|
|
101362
|
+
}
|
|
101359
101363
|
async function promptScope(ctx) {
|
|
101360
101364
|
const scope2 = await _t({
|
|
101361
101365
|
message: "secret scope",
|
|
@@ -101579,7 +101583,8 @@ async function runCodexAuth() {
|
|
|
101579
101583
|
O2.warn(err instanceof Error ? err.message : String(err));
|
|
101580
101584
|
savable = auth2;
|
|
101581
101585
|
}
|
|
101582
|
-
|
|
101586
|
+
const target = describeSecretTarget({ owner: remote.owner, repo: remote.repo, scope: scope2 });
|
|
101587
|
+
spin.start(`saving ${import_picocolors2.default.cyan(CODEX_AUTH_SECRET)} to ${target}`);
|
|
101583
101588
|
const result = await setPullfrogSecret({
|
|
101584
101589
|
token,
|
|
101585
101590
|
owner: remote.owner,
|
|
@@ -101596,7 +101601,7 @@ async function runCodexAuth() {
|
|
|
101596
101601
|
);
|
|
101597
101602
|
process.exit(1);
|
|
101598
101603
|
}
|
|
101599
|
-
spin.stop(`saved ${import_picocolors2.default.cyan(CODEX_AUTH_SECRET)} to
|
|
101604
|
+
spin.stop(`saved ${import_picocolors2.default.cyan(CODEX_AUTH_SECRET)} to ${target}`);
|
|
101600
101605
|
setActiveSpin(null);
|
|
101601
101606
|
gt("done.");
|
|
101602
101607
|
} catch (error49) {
|
|
@@ -101693,7 +101698,8 @@ async function runClaudeAuth() {
|
|
|
101693
101698
|
`that doesn't look like a ${import_picocolors2.default.cyan("claude setup-token")} token (expected ${import_picocolors2.default.cyan(`${CLAUDE_OAUTH_TOKEN_PREFIX}\u2026`)}). saving it anyway.`
|
|
101694
101699
|
);
|
|
101695
101700
|
}
|
|
101696
|
-
|
|
101701
|
+
const target = describeSecretTarget({ owner: remote.owner, repo: remote.repo, scope: scope2 });
|
|
101702
|
+
spin.start(`saving ${import_picocolors2.default.cyan(CLAUDE_OAUTH_SECRET)} to ${target}`);
|
|
101697
101703
|
const result = await setPullfrogSecret({
|
|
101698
101704
|
token,
|
|
101699
101705
|
owner: remote.owner,
|
|
@@ -101710,7 +101716,7 @@ async function runClaudeAuth() {
|
|
|
101710
101716
|
);
|
|
101711
101717
|
process.exit(1);
|
|
101712
101718
|
}
|
|
101713
|
-
spin.stop(`saved ${import_picocolors2.default.cyan(CLAUDE_OAUTH_SECRET)} to
|
|
101719
|
+
spin.stop(`saved ${import_picocolors2.default.cyan(CLAUDE_OAUTH_SECRET)} to ${target}`);
|
|
101714
101720
|
setActiveSpin(null);
|
|
101715
101721
|
gt("done.");
|
|
101716
101722
|
} catch (error49) {
|
|
@@ -102119,6 +102125,28 @@ var providers = {
|
|
|
102119
102125
|
}
|
|
102120
102126
|
}
|
|
102121
102127
|
}),
|
|
102128
|
+
"openai-compatible": provider({
|
|
102129
|
+
// "Custom" is the picker group, "OpenAI-compatible" the entry under it, so the
|
|
102130
|
+
// menu reads `Custom › OpenAI-compatible` and a second custom backend (a
|
|
102131
|
+
// different wire format, say) slots in beside it without a rename. the
|
|
102132
|
+
// provider KEY stays `openai-compatible` — it's the stored slug and the
|
|
102133
|
+
// `OPENAI_COMPATIBLE_*` env prefix, so this is display-only.
|
|
102134
|
+
displayName: "Custom",
|
|
102135
|
+
// bring-your-own generic OpenAI-compatible endpoint — Cloudflare AI Gateway,
|
|
102136
|
+
// Alibaba DashScope, self-hosted vLLM, or any compatible gateway. base URL +
|
|
102137
|
+
// key + model ID are all supplied via env; nothing is cataloged or bumped.
|
|
102138
|
+
envVars: ["OPENAI_COMPATIBLE_BASE_URL", "OPENAI_COMPATIBLE_API_KEY", "OPENAI_COMPATIBLE_MODEL"],
|
|
102139
|
+
models: {
|
|
102140
|
+
// single routing entry — the actual model ID is read from
|
|
102141
|
+
// OPENAI_COMPATIBLE_MODEL at run time and the provider is materialized
|
|
102142
|
+
// via `@ai-sdk/openai-compatible`.
|
|
102143
|
+
byok: {
|
|
102144
|
+
displayName: "OpenAI-compatible",
|
|
102145
|
+
resolve: "openai-compatible",
|
|
102146
|
+
routing: "openai-compatible"
|
|
102147
|
+
}
|
|
102148
|
+
}
|
|
102149
|
+
}),
|
|
102122
102150
|
openrouter: provider({
|
|
102123
102151
|
displayName: "OpenRouter",
|
|
102124
102152
|
envVars: ["OPENROUTER_API_KEY"],
|
|
@@ -102336,6 +102364,12 @@ var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
|
|
|
102336
102364
|
var defaultProxyDisplayName = defaultProxyAlias.displayName;
|
|
102337
102365
|
var BEDROCK_MODEL_ID_ENV = "BEDROCK_MODEL_ID";
|
|
102338
102366
|
var VERTEX_MODEL_ID_ENV = "VERTEX_MODEL_ID";
|
|
102367
|
+
var OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
|
|
102368
|
+
var OPENAI_COMPATIBLE_BASE_URL_ENV = "OPENAI_COMPATIBLE_BASE_URL";
|
|
102369
|
+
var OPENAI_COMPATIBLE_API_KEY_ENV = "OPENAI_COMPATIBLE_API_KEY";
|
|
102370
|
+
var OPENAI_COMPATIBLE_MODEL_ENV = "OPENAI_COMPATIBLE_MODEL";
|
|
102371
|
+
var OPENAI_COMPATIBLE_CONTEXT_ENV = "OPENAI_COMPATIBLE_CONTEXT";
|
|
102372
|
+
var OPENAI_COMPATIBLE_MAX_OUTPUT_ENV = "OPENAI_COMPATIBLE_MAX_OUTPUT";
|
|
102339
102373
|
function isBedrockAnthropicId(bedrockModelId) {
|
|
102340
102374
|
return bedrockModelId.toLowerCase().split(/[./:]/).includes("anthropic");
|
|
102341
102375
|
}
|
|
@@ -103022,7 +103056,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
103022
103056
|
// package.json
|
|
103023
103057
|
var package_default = {
|
|
103024
103058
|
name: "pullfrog",
|
|
103025
|
-
version: "0.1.
|
|
103059
|
+
version: "0.1.47",
|
|
103026
103060
|
type: "module",
|
|
103027
103061
|
bin: {
|
|
103028
103062
|
pullfrog: "dist/cli.mjs",
|
|
@@ -110769,8 +110803,9 @@ function readModels(cliPath) {
|
|
|
110769
110803
|
stdio: ["ignore", "pipe", "pipe"]
|
|
110770
110804
|
});
|
|
110771
110805
|
if (result.status !== 0) {
|
|
110772
|
-
|
|
110773
|
-
|
|
110806
|
+
const stderr = (result.stderr ?? "").replace(ANSI_PATTERN, "").trim();
|
|
110807
|
+
failure = stderr || result.error?.message;
|
|
110808
|
+
log.debug(`\xBB \`opencode models\` failed (${result.status}): ${failure}`);
|
|
110774
110809
|
return /* @__PURE__ */ new Set();
|
|
110775
110810
|
}
|
|
110776
110811
|
failure = void 0;
|
|
@@ -110828,6 +110863,29 @@ function geminiHighThinkingOverrides() {
|
|
|
110828
110863
|
])
|
|
110829
110864
|
);
|
|
110830
110865
|
}
|
|
110866
|
+
function openAICompatibleLimit() {
|
|
110867
|
+
return {
|
|
110868
|
+
context: Number(process.env[OPENAI_COMPATIBLE_CONTEXT_ENV]),
|
|
110869
|
+
output: Number(process.env[OPENAI_COMPATIBLE_MAX_OUTPUT_ENV])
|
|
110870
|
+
};
|
|
110871
|
+
}
|
|
110872
|
+
function openAICompatibleProvider(model) {
|
|
110873
|
+
const prefix = `${OPENAI_COMPATIBLE_PROVIDER}/`;
|
|
110874
|
+
if (!model?.startsWith(prefix)) return {};
|
|
110875
|
+
const modelId = model.slice(prefix.length);
|
|
110876
|
+
return {
|
|
110877
|
+
[OPENAI_COMPATIBLE_PROVIDER]: {
|
|
110878
|
+
npm: "@ai-sdk/openai-compatible",
|
|
110879
|
+
name: "OpenAI-compatible",
|
|
110880
|
+
options: {
|
|
110881
|
+
// trailing slash would double up against opencode's `/chat/completions` join
|
|
110882
|
+
baseURL: process.env[OPENAI_COMPATIBLE_BASE_URL_ENV]?.replace(/\/$/, ""),
|
|
110883
|
+
apiKey: process.env[OPENAI_COMPATIBLE_API_KEY_ENV]
|
|
110884
|
+
},
|
|
110885
|
+
models: { [modelId]: { name: modelId, limit: openAICompatibleLimit() } }
|
|
110886
|
+
}
|
|
110887
|
+
};
|
|
110888
|
+
}
|
|
110831
110889
|
var KIMI_ENFORCERLESS_PROVIDERS = ["siliconflow", "together"];
|
|
110832
110890
|
function kimiOpenRouterProviderOverrides() {
|
|
110833
110891
|
return Object.fromEntries(
|
|
@@ -110919,7 +110977,8 @@ function buildSecurityConfig(ctx, model) {
|
|
|
110919
110977
|
google: { models: geminiHighThinkingOverrides() },
|
|
110920
110978
|
openrouter: {
|
|
110921
110979
|
models: { ...deepseekHighEffortOverrides(), ...kimiOpenRouterProviderOverrides() }
|
|
110922
|
-
}
|
|
110980
|
+
},
|
|
110981
|
+
...openAICompatibleProvider(model)
|
|
110923
110982
|
}
|
|
110924
110983
|
};
|
|
110925
110984
|
if (model) {
|
|
@@ -159914,6 +159973,9 @@ var JsonPayload = type({
|
|
|
159914
159973
|
id: "string",
|
|
159915
159974
|
type: "'issue' | 'review'"
|
|
159916
159975
|
}).or("undefined"),
|
|
159976
|
+
// optional so a payload from an older server build (pre-`checkRun`) still parses
|
|
159977
|
+
// against a newer action across a rolling deploy.
|
|
159978
|
+
"checkRun?": type({ id: "string" }).or("undefined"),
|
|
159917
159979
|
"generateSummary?": "boolean | undefined"
|
|
159918
159980
|
});
|
|
159919
159981
|
var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
|
|
@@ -160027,13 +160089,19 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
|
|
|
160027
160089
|
timeout: inputs.timeout ?? jsonPayload?.timeout,
|
|
160028
160090
|
cwd: resolveCwd(inputs.cwd),
|
|
160029
160091
|
progressComment: jsonPayload?.progressComment,
|
|
160092
|
+
checkRun: jsonPayload?.checkRun,
|
|
160030
160093
|
generateSummary: jsonPayload?.generateSummary,
|
|
160031
160094
|
// permissions: inputs > repoSettings > fallbacks
|
|
160032
160095
|
push: inputs.push ?? repoSettings.push ?? "restricted",
|
|
160033
160096
|
shell: resolvedShell,
|
|
160034
|
-
//
|
|
160035
|
-
//
|
|
160036
|
-
|
|
160097
|
+
// the `pullfrog` run-lifecycle check. ON by default — the whole point is that a PR
|
|
160098
|
+
// shows whether Pullfrog is running without anyone having to opt in. the workflow
|
|
160099
|
+
// input is the source of truth when set (mirrors `push`); otherwise the repo
|
|
160100
|
+
// setting decides.
|
|
160101
|
+
runStatusCheck: inputs.status_checks === void 0 ? repoSettings.statusChecks : inputs.status_checks === "enabled",
|
|
160102
|
+
// the `pullfrog-approval` verdict check stays opt-in and workflow-only. it exists to
|
|
160103
|
+
// be *required* by branch protection, so it must never turn itself on.
|
|
160104
|
+
approvalCheck: inputs.status_checks === "enabled",
|
|
160037
160105
|
// temporary progress chrome. the workflow input is the source of truth when
|
|
160038
160106
|
// set (mirrors `push`); otherwise the repo setting decides. defaults to true.
|
|
160039
160107
|
progressComments: inputs.progress_comments === void 0 ? repoSettings.progressComments : inputs.progress_comments === "enabled",
|
|
@@ -160561,7 +160629,11 @@ async function approveAfterFix(ctx) {
|
|
|
160561
160629
|
approved: true,
|
|
160562
160630
|
hasComments: false
|
|
160563
160631
|
});
|
|
160564
|
-
ctx.toolState.approval = {
|
|
160632
|
+
ctx.toolState.approval = {
|
|
160633
|
+
wouldApprove: true,
|
|
160634
|
+
sha: headSha,
|
|
160635
|
+
url: result.data.html_url
|
|
160636
|
+
};
|
|
160565
160637
|
log.info(`\xBB auto-approved #${pullNumber} after fix run (review ${result.data.id})`);
|
|
160566
160638
|
await deleteProgressComment(ctx).catch((err) => {
|
|
160567
160639
|
log.debug(`progress comment cleanup after fix auto-approval failed: ${err}`);
|
|
@@ -160894,6 +160966,7 @@ function CreatePullRequestReviewTool(ctx) {
|
|
|
160894
160966
|
}
|
|
160895
160967
|
const reviewId = result.data.id;
|
|
160896
160968
|
const reviewNodeId = result.data.node_id;
|
|
160969
|
+
if (ctx.toolState.approval) ctx.toolState.approval.url = result.data.html_url;
|
|
160897
160970
|
log.info(`\xBB created review ${reviewId} on pull request #${pull_number}`);
|
|
160898
160971
|
const actuallyReviewedSha = primary.checkoutSha ?? params.commit_id;
|
|
160899
160972
|
ctx.toolState.review = {
|
|
@@ -165185,6 +165258,15 @@ function resolveSlug(slug2) {
|
|
|
165185
165258
|
}
|
|
165186
165259
|
return vertexId;
|
|
165187
165260
|
}
|
|
165261
|
+
if (alias?.routing === "openai-compatible") {
|
|
165262
|
+
const modelId = process.env[OPENAI_COMPATIBLE_MODEL_ENV]?.trim();
|
|
165263
|
+
if (!modelId) {
|
|
165264
|
+
throw new Error(
|
|
165265
|
+
`${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.`
|
|
165266
|
+
);
|
|
165267
|
+
}
|
|
165268
|
+
return `${OPENAI_COMPATIBLE_PROVIDER}/${modelId}`;
|
|
165269
|
+
}
|
|
165188
165270
|
return resolveCliModel(slug2);
|
|
165189
165271
|
}
|
|
165190
165272
|
function resolveModel(ctx) {
|
|
@@ -165234,6 +165316,21 @@ function resolveAgent(ctx) {
|
|
|
165234
165316
|
|
|
165235
165317
|
// utils/apiKeys.ts
|
|
165236
165318
|
var MISSING_KEY_MARKER = "no API key found";
|
|
165319
|
+
var SECRETS_UNAVAILABLE_MARKER = "couldn't load your Pullfrog secrets";
|
|
165320
|
+
function buildKeyError(params) {
|
|
165321
|
+
return params.secretsUnavailable ? buildSecretsUnavailableError(params) : buildMissingApiKeyError(params);
|
|
165322
|
+
}
|
|
165323
|
+
function buildSecretsUnavailableError(params) {
|
|
165324
|
+
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
165325
|
+
const modelClause = params.model ? ` needed for \`${params.model}\`` : "";
|
|
165326
|
+
return [
|
|
165327
|
+
`**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.`,
|
|
165328
|
+
"",
|
|
165329
|
+
"**To fix:** re-run the job. If it keeps happening, tell us in Discord.",
|
|
165330
|
+
"",
|
|
165331
|
+
`[Model settings \u2192](${settingsUrl}) \xB7 [Setup docs \u2192](https://docs.pullfrog.com/keys) \xB7 [Ask in Discord \u2192](https://discord.gg/8y96raFg8e)`
|
|
165332
|
+
].join("\n");
|
|
165333
|
+
}
|
|
165237
165334
|
function buildMissingApiKeyError(params) {
|
|
165238
165335
|
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
165239
165336
|
const settingsUrl = `${getApiUrl()}/console/${params.owner}/${params.name}`;
|
|
@@ -165276,10 +165373,47 @@ add the missing secret(s) to your GitHub repository at ${githubSecretsUrl}, then
|
|
|
165276
165373
|
|
|
165277
165374
|
for full setup instructions, see https://docs.pullfrog.com/vertex`;
|
|
165278
165375
|
}
|
|
165376
|
+
function buildOpenAICompatibleSetupError(params) {
|
|
165377
|
+
const githubSecretsUrl = `https://github.com/${params.owner}/${params.name}/settings/secrets/actions`;
|
|
165378
|
+
return `OpenAI-compatible model selected but required configuration is missing: ${params.missing.join(", ")}.
|
|
165379
|
+
|
|
165380
|
+
only the API key is sensitive \u2014 add it as a secret at ${githubSecretsUrl}. everything else is plain workflow \`env:\`:
|
|
165381
|
+
|
|
165382
|
+
${OPENAI_COMPATIBLE_BASE_URL_ENV}: https://your-endpoint.example.com/v1
|
|
165383
|
+
${OPENAI_COMPATIBLE_API_KEY_ENV}: \${{ secrets.${OPENAI_COMPATIBLE_API_KEY_ENV} }}
|
|
165384
|
+
${OPENAI_COMPATIBLE_MODEL_ENV}: <model-id>
|
|
165385
|
+
${OPENAI_COMPATIBLE_CONTEXT_ENV}: "128000"
|
|
165386
|
+
${OPENAI_COMPATIBLE_MAX_OUTPUT_ENV}: "16384"
|
|
165387
|
+
|
|
165388
|
+
set the last two to the real limits of the model your endpoint serves. Pullfrog can't
|
|
165389
|
+
discover them \u2014 your endpoint owns the model catalog \u2014 and without them completions are
|
|
165390
|
+
capped at 32000 tokens (rejected outright by models with a smaller cap) and
|
|
165391
|
+
auto-compaction is disabled, so long runs grow until your endpoint refuses them.
|
|
165392
|
+
|
|
165393
|
+
for full setup instructions, see https://docs.pullfrog.com/openai-compatible`;
|
|
165394
|
+
}
|
|
165279
165395
|
function hasEnvVar3(name) {
|
|
165280
165396
|
const value2 = process.env[name];
|
|
165281
165397
|
return typeof value2 === "string" && value2.length > 0;
|
|
165282
165398
|
}
|
|
165399
|
+
function hasPositiveNumberEnvVar(name) {
|
|
165400
|
+
return Number(process.env[name]) > 0;
|
|
165401
|
+
}
|
|
165402
|
+
function validateOpenAICompatibleSetup(params) {
|
|
165403
|
+
const missing = [];
|
|
165404
|
+
if (!hasEnvVar3(OPENAI_COMPATIBLE_BASE_URL_ENV)) missing.push(OPENAI_COMPATIBLE_BASE_URL_ENV);
|
|
165405
|
+
if (!hasEnvVar3(OPENAI_COMPATIBLE_API_KEY_ENV)) missing.push(OPENAI_COMPATIBLE_API_KEY_ENV);
|
|
165406
|
+
if (!hasEnvVar3(OPENAI_COMPATIBLE_MODEL_ENV)) missing.push(OPENAI_COMPATIBLE_MODEL_ENV);
|
|
165407
|
+
if (!hasPositiveNumberEnvVar(OPENAI_COMPATIBLE_CONTEXT_ENV))
|
|
165408
|
+
missing.push(OPENAI_COMPATIBLE_CONTEXT_ENV);
|
|
165409
|
+
if (!hasPositiveNumberEnvVar(OPENAI_COMPATIBLE_MAX_OUTPUT_ENV))
|
|
165410
|
+
missing.push(OPENAI_COMPATIBLE_MAX_OUTPUT_ENV);
|
|
165411
|
+
if (missing.length > 0) {
|
|
165412
|
+
throw new Error(
|
|
165413
|
+
buildOpenAICompatibleSetupError({ owner: params.owner, name: params.name, missing })
|
|
165414
|
+
);
|
|
165415
|
+
}
|
|
165416
|
+
}
|
|
165283
165417
|
function validateBedrockSetup(params) {
|
|
165284
165418
|
const hasAuth = hasEnvVar3("AWS_BEARER_TOKEN_BEDROCK") || hasEnvVar3("AWS_ACCESS_KEY_ID") && hasEnvVar3("AWS_SECRET_ACCESS_KEY");
|
|
165285
165419
|
const missing = [];
|
|
@@ -165314,6 +165448,10 @@ function validateAgentApiKey(params) {
|
|
|
165314
165448
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
165315
165449
|
return;
|
|
165316
165450
|
}
|
|
165451
|
+
if (params.model.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`)) {
|
|
165452
|
+
validateOpenAICompatibleSetup({ owner: params.owner, name: params.name });
|
|
165453
|
+
return;
|
|
165454
|
+
}
|
|
165317
165455
|
if (!params.model.includes("/")) {
|
|
165318
165456
|
if (process.env[VERTEX_MODEL_ID_ENV]?.trim() === params.model) {
|
|
165319
165457
|
validateVertexSetup({ owner: params.owner, name: params.name });
|
|
@@ -165326,23 +165464,46 @@ function validateAgentApiKey(params) {
|
|
|
165326
165464
|
if (params.authorized.has(params.model)) return;
|
|
165327
165465
|
const reason = getModelsFailure();
|
|
165328
165466
|
if (reason) throw new Error(reason);
|
|
165467
|
+
if (getModelEnvVars(params.model).some(hasEnvVar3)) return;
|
|
165329
165468
|
throw new Error(
|
|
165330
|
-
|
|
165469
|
+
buildKeyError({
|
|
165470
|
+
owner: params.owner,
|
|
165471
|
+
name: params.name,
|
|
165472
|
+
model: params.model,
|
|
165473
|
+
secretsUnavailable: params.secretsUnavailable
|
|
165474
|
+
})
|
|
165331
165475
|
);
|
|
165332
165476
|
}
|
|
165333
165477
|
if (hasEnvVar3("ANTHROPIC_API_KEY") || hasEnvVar3("CLAUDE_CODE_OAUTH_TOKEN")) return;
|
|
165334
165478
|
throw new Error(
|
|
165335
|
-
|
|
165479
|
+
buildKeyError({
|
|
165480
|
+
owner: params.owner,
|
|
165481
|
+
name: params.name,
|
|
165482
|
+
model: params.model,
|
|
165483
|
+
secretsUnavailable: params.secretsUnavailable
|
|
165484
|
+
})
|
|
165336
165485
|
);
|
|
165337
165486
|
}
|
|
165338
165487
|
if (params.agent.name === "opencode") {
|
|
165339
165488
|
if (params.authorized.size > 0) return;
|
|
165340
165489
|
const reason = getModelsFailure();
|
|
165341
165490
|
if (reason) throw new Error(reason);
|
|
165342
|
-
throw new Error(
|
|
165491
|
+
throw new Error(
|
|
165492
|
+
buildKeyError({
|
|
165493
|
+
owner: params.owner,
|
|
165494
|
+
name: params.name,
|
|
165495
|
+
secretsUnavailable: params.secretsUnavailable
|
|
165496
|
+
})
|
|
165497
|
+
);
|
|
165343
165498
|
}
|
|
165344
165499
|
if (hasEnvVar3("ANTHROPIC_API_KEY") || hasEnvVar3("CLAUDE_CODE_OAUTH_TOKEN")) return;
|
|
165345
|
-
throw new Error(
|
|
165500
|
+
throw new Error(
|
|
165501
|
+
buildKeyError({
|
|
165502
|
+
owner: params.owner,
|
|
165503
|
+
name: params.name,
|
|
165504
|
+
secretsUnavailable: params.secretsUnavailable
|
|
165505
|
+
})
|
|
165506
|
+
);
|
|
165346
165507
|
}
|
|
165347
165508
|
function isApiKeyAuthError(text) {
|
|
165348
165509
|
if (!text) return false;
|
|
@@ -166027,7 +166188,7 @@ async function persistXrepoLearnings(ctx) {
|
|
|
166027
166188
|
// utils/modelAccess.ts
|
|
166028
166189
|
function decideModelAccess(input) {
|
|
166029
166190
|
if (!input.modelExplicit || !input.model) return { kind: "ok" };
|
|
166030
|
-
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/"));
|
|
166191
|
+
const byokAuthorized = !!input.resolvedModel && (input.authorized.has(input.resolvedModel) || !input.resolvedModel.includes("/") || input.resolvedModel.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`));
|
|
166031
166192
|
if (input.proxyActive) {
|
|
166032
166193
|
const target = resolveOpenRouterModel(input.model);
|
|
166033
166194
|
if (input.oss) {
|
|
@@ -166822,6 +166983,7 @@ var defaultSettings = {
|
|
|
166822
166983
|
autoMergeEnabled: false,
|
|
166823
166984
|
signedCommits: false,
|
|
166824
166985
|
progressComments: true,
|
|
166986
|
+
statusChecks: true,
|
|
166825
166987
|
modeInstructions: {},
|
|
166826
166988
|
learnings: null,
|
|
166827
166989
|
learningsHeadings: [],
|
|
@@ -166836,6 +166998,10 @@ var defaultRunContext = {
|
|
|
166836
166998
|
oss: false,
|
|
166837
166999
|
plan: "none"
|
|
166838
167000
|
};
|
|
167001
|
+
var unknownSecretsRunContext = {
|
|
167002
|
+
...defaultRunContext,
|
|
167003
|
+
secretsUnavailable: true
|
|
167004
|
+
};
|
|
166839
167005
|
async function fetchRunContext(params) {
|
|
166840
167006
|
const timeoutMs = 3e4;
|
|
166841
167007
|
const controller = new AbortController();
|
|
@@ -166854,7 +167020,7 @@ async function fetchRunContext(params) {
|
|
|
166854
167020
|
});
|
|
166855
167021
|
clearTimeout(timeoutId);
|
|
166856
167022
|
if (!response.ok) {
|
|
166857
|
-
return defaultRunContext;
|
|
167023
|
+
return response.status >= 500 ? unknownSecretsRunContext : defaultRunContext;
|
|
166858
167024
|
}
|
|
166859
167025
|
const data = await response.json();
|
|
166860
167026
|
if (data === null) {
|
|
@@ -166878,11 +167044,12 @@ async function fetchRunContext(params) {
|
|
|
166878
167044
|
oss: data.oss ?? false,
|
|
166879
167045
|
plan: data.plan ?? "none",
|
|
166880
167046
|
proxyModel: data.proxyModel,
|
|
166881
|
-
dbSecrets: data.dbSecrets
|
|
167047
|
+
dbSecrets: data.dbSecrets,
|
|
167048
|
+
secretsUnavailable: data.secretsUnavailable
|
|
166882
167049
|
};
|
|
166883
167050
|
} catch {
|
|
166884
167051
|
clearTimeout(timeoutId);
|
|
166885
|
-
return
|
|
167052
|
+
return unknownSecretsRunContext;
|
|
166886
167053
|
}
|
|
166887
167054
|
}
|
|
166888
167055
|
|
|
@@ -166904,7 +167071,10 @@ async function resolveRunContextData(params) {
|
|
|
166904
167071
|
const repoContext = parseRepoContext();
|
|
166905
167072
|
let oidcToken;
|
|
166906
167073
|
try {
|
|
166907
|
-
oidcToken = await core9.getIDToken("pullfrog-api")
|
|
167074
|
+
oidcToken = await op(() => core9.getIDToken("pullfrog-api"), {
|
|
167075
|
+
name: "OIDC mint",
|
|
167076
|
+
retries: process.env.ACTIONS_ID_TOKEN_REQUEST_URL ? [200, 1e3] : []
|
|
167077
|
+
})();
|
|
166908
167078
|
} catch {
|
|
166909
167079
|
}
|
|
166910
167080
|
const [repoResponse, runContext] = await Promise.all([
|
|
@@ -166926,7 +167096,10 @@ async function resolveRunContextData(params) {
|
|
|
166926
167096
|
oss: runContext.oss,
|
|
166927
167097
|
plan: runContext.plan,
|
|
166928
167098
|
proxyModel: runContext.proxyModel,
|
|
166929
|
-
dbSecrets: runContext.dbSecrets
|
|
167099
|
+
dbSecrets: runContext.dbSecrets,
|
|
167100
|
+
// a failed mint on a runner that should have been able to mint is the same
|
|
167101
|
+
// outcome as the server-side failure: the run never sees stored secrets.
|
|
167102
|
+
secretsUnavailable: runContext.secretsUnavailable || !!process.env.ACTIONS_ID_TOKEN_REQUEST_URL && oidcToken === void 0
|
|
166930
167103
|
};
|
|
166931
167104
|
}
|
|
166932
167105
|
|
|
@@ -167091,6 +167264,9 @@ function renderRunError(input) {
|
|
|
167091
167264
|
if (input.errorMessage.includes(MODEL_ACCESS_MARKER)) {
|
|
167092
167265
|
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
167093
167266
|
}
|
|
167267
|
+
if (input.errorMessage.includes(SECRETS_UNAVAILABLE_MARKER)) {
|
|
167268
|
+
return { summary: input.errorMessage, comment: input.errorMessage };
|
|
167269
|
+
}
|
|
167094
167270
|
const isHang = input.errorMessage.startsWith("activity timeout") || input.errorMessage.startsWith("agent still pending");
|
|
167095
167271
|
const hangBody = isHang ? formatAgentHangBody({
|
|
167096
167272
|
diagnostic: input.agentDiagnostic,
|
|
@@ -167329,30 +167505,123 @@ function getCurrentWorkflowFilename() {
|
|
|
167329
167505
|
return match3?.[1] ?? "pullfrog.yml";
|
|
167330
167506
|
}
|
|
167331
167507
|
|
|
167332
|
-
// utils/
|
|
167333
|
-
var
|
|
167334
|
-
var
|
|
167335
|
-
|
|
167508
|
+
// utils/runStatusCheck.ts
|
|
167509
|
+
var RUN_STATUS_CHECK_NAME = "Pullfrog";
|
|
167510
|
+
var APPROVAL_CHECK_NAME = "Pullfrog approval";
|
|
167511
|
+
function parseCheckRunId(raw2) {
|
|
167512
|
+
if (!raw2?.id) return void 0;
|
|
167513
|
+
const id = parseInt(raw2.id, 10);
|
|
167514
|
+
if (Number.isNaN(id) || id <= 0) return void 0;
|
|
167515
|
+
return id;
|
|
167516
|
+
}
|
|
167517
|
+
function disableCheckLine(owner, repo) {
|
|
167518
|
+
const url4 = `https://pullfrog.com/console/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}#auto-review-prs`;
|
|
167519
|
+
return `
|
|
167520
|
+
|
|
167521
|
+
[Turn off this check \u2192](${url4}) \u2014 it reports run status only and gates nothing unless you required it in branch protection.`;
|
|
167522
|
+
}
|
|
167523
|
+
var TERMINAL_OUTPUT = {
|
|
167524
|
+
success: {
|
|
167525
|
+
title: "Pullfrog run completed",
|
|
167526
|
+
summary: "The Pullfrog run finished successfully."
|
|
167527
|
+
},
|
|
167528
|
+
failure: {
|
|
167529
|
+
title: "Pullfrog run failed",
|
|
167530
|
+
summary: "The Pullfrog run failed. See the run logs for details."
|
|
167531
|
+
},
|
|
167532
|
+
cancelled: {
|
|
167533
|
+
title: "Pullfrog run cancelled",
|
|
167534
|
+
summary: "The Pullfrog run was cancelled before it finished."
|
|
167535
|
+
},
|
|
167536
|
+
timed_out: {
|
|
167537
|
+
title: "Pullfrog run timed out",
|
|
167538
|
+
summary: "The Pullfrog run exceeded its timeout. See the run logs for details."
|
|
167539
|
+
},
|
|
167540
|
+
action_required: {
|
|
167541
|
+
title: "Pullfrog run needs attention",
|
|
167542
|
+
summary: "The Pullfrog run stopped and needs attention. See the run logs for details."
|
|
167543
|
+
},
|
|
167544
|
+
neutral: {
|
|
167545
|
+
title: "Pullfrog run finished",
|
|
167546
|
+
summary: "The Pullfrog run finished without a pass or fail outcome."
|
|
167547
|
+
},
|
|
167548
|
+
skipped: {
|
|
167549
|
+
title: "Pullfrog run skipped",
|
|
167550
|
+
summary: "This run was superseded by another Pullfrog run."
|
|
167551
|
+
},
|
|
167552
|
+
stale: {
|
|
167553
|
+
title: "Pullfrog run didn't finish",
|
|
167554
|
+
summary: "Pullfrog never received a completion signal for this run. See the run logs for details."
|
|
167555
|
+
}
|
|
167556
|
+
};
|
|
167557
|
+
function terminalOutput(params) {
|
|
167558
|
+
const base = TERMINAL_OUTPUT[params.conclusion];
|
|
167559
|
+
const review = params.reviewUrl ? `
|
|
167560
|
+
|
|
167561
|
+
[View the review Pullfrog posted \u2192](${params.reviewUrl})` : "";
|
|
167562
|
+
const disable = params.conclusion === "success" ? "" : disableCheckLine(params.owner, params.repo);
|
|
167563
|
+
return { title: base.title, summary: base.summary + review + disable };
|
|
167564
|
+
}
|
|
167565
|
+
async function finalizeRunStatusCheck(params) {
|
|
167566
|
+
const updateParams = {
|
|
167567
|
+
owner: params.owner,
|
|
167568
|
+
repo: params.repo,
|
|
167569
|
+
check_run_id: params.checkRunId,
|
|
167570
|
+
status: "completed",
|
|
167571
|
+
conclusion: params.conclusion,
|
|
167572
|
+
output: terminalOutput({
|
|
167573
|
+
conclusion: params.conclusion,
|
|
167574
|
+
owner: params.owner,
|
|
167575
|
+
repo: params.repo,
|
|
167576
|
+
reviewUrl: params.reviewUrl
|
|
167577
|
+
})
|
|
167578
|
+
};
|
|
167579
|
+
if (params.detailsUrl) updateParams.details_url = params.detailsUrl;
|
|
167580
|
+
await params.octokit.rest.checks.update(updateParams);
|
|
167581
|
+
}
|
|
167582
|
+
async function createTerminalRunStatusCheck(params) {
|
|
167336
167583
|
const createParams = {
|
|
167337
|
-
owner:
|
|
167338
|
-
repo:
|
|
167339
|
-
name:
|
|
167584
|
+
owner: params.owner,
|
|
167585
|
+
repo: params.repo,
|
|
167586
|
+
name: RUN_STATUS_CHECK_NAME,
|
|
167340
167587
|
head_sha: params.headSha,
|
|
167341
167588
|
status: "completed",
|
|
167342
167589
|
conclusion: params.conclusion,
|
|
167343
|
-
output: {
|
|
167590
|
+
output: terminalOutput({
|
|
167591
|
+
conclusion: params.conclusion,
|
|
167592
|
+
owner: params.owner,
|
|
167593
|
+
repo: params.repo,
|
|
167594
|
+
reviewUrl: params.reviewUrl
|
|
167595
|
+
})
|
|
167344
167596
|
};
|
|
167345
|
-
if (
|
|
167346
|
-
|
|
167347
|
-
}
|
|
167348
|
-
await ctx.octokit.rest.checks.create(createParams);
|
|
167349
|
-
log.info(`\xBB posted ${params.name} check (${params.conclusion}) on ${params.headSha.slice(0, 7)}`);
|
|
167597
|
+
if (params.detailsUrl) createParams.details_url = params.detailsUrl;
|
|
167598
|
+
await params.octokit.rest.checks.create(createParams);
|
|
167350
167599
|
}
|
|
167600
|
+
|
|
167601
|
+
// utils/statusChecks.ts
|
|
167351
167602
|
async function reportStatusChecks(ctx, params) {
|
|
167352
|
-
if (!ctx.payload.statusChecks) return;
|
|
167353
167603
|
const event = ctx.payload.event;
|
|
167354
167604
|
const pullNumber = event.issue_number;
|
|
167355
167605
|
if (event.is_pr !== true || typeof pullNumber !== "number") return;
|
|
167606
|
+
const checkRunId = parseCheckRunId(ctx.payload.checkRun);
|
|
167607
|
+
if (checkRunId === void 0 && !ctx.payload.runStatusCheck && !ctx.payload.approvalCheck) return;
|
|
167608
|
+
const conclusion = params.runSucceeded ? "success" : "failure";
|
|
167609
|
+
const detailsUrl = ctx.runId ? `https://github.com/${ctx.repo.owner}/${ctx.repo.name}/actions/runs/${ctx.runId}` : void 0;
|
|
167610
|
+
if (checkRunId !== void 0) {
|
|
167611
|
+
await finalizeRunStatusCheck({
|
|
167612
|
+
octokit: ctx.octokit,
|
|
167613
|
+
owner: ctx.repo.owner,
|
|
167614
|
+
repo: ctx.repo.name,
|
|
167615
|
+
checkRunId,
|
|
167616
|
+
conclusion,
|
|
167617
|
+
detailsUrl,
|
|
167618
|
+
reviewUrl: ctx.toolState.approval?.url
|
|
167619
|
+
}).then(() => log.info(`\xBB finalized ${RUN_STATUS_CHECK_NAME} check (${conclusion})`)).catch((err) => log.debug(`status checks: ${RUN_STATUS_CHECK_NAME} finalize failed: ${err}`));
|
|
167620
|
+
}
|
|
167621
|
+
const approval = ctx.toolState.approval;
|
|
167622
|
+
const needsApprovalCheck = ctx.payload.approvalCheck && params.runSucceeded && approval;
|
|
167623
|
+
const needsFallbackRunCheck = ctx.payload.runStatusCheck && checkRunId === void 0;
|
|
167624
|
+
if (!needsApprovalCheck && !needsFallbackRunCheck) return;
|
|
167356
167625
|
let headSha;
|
|
167357
167626
|
try {
|
|
167358
167627
|
const pr = await ctx.octokit.rest.pulls.get({
|
|
@@ -167365,24 +167634,32 @@ async function reportStatusChecks(ctx, params) {
|
|
|
167365
167634
|
log.debug(`status checks: failed to resolve PR #${pullNumber} head sha: ${err}`);
|
|
167366
167635
|
return;
|
|
167367
167636
|
}
|
|
167368
|
-
|
|
167369
|
-
|
|
167370
|
-
|
|
167371
|
-
|
|
167372
|
-
|
|
167373
|
-
|
|
167374
|
-
|
|
167375
|
-
|
|
167376
|
-
|
|
167377
|
-
|
|
167378
|
-
|
|
167379
|
-
|
|
167380
|
-
|
|
167381
|
-
|
|
167637
|
+
if (needsFallbackRunCheck) {
|
|
167638
|
+
await createTerminalRunStatusCheck({
|
|
167639
|
+
octokit: ctx.octokit,
|
|
167640
|
+
owner: ctx.repo.owner,
|
|
167641
|
+
repo: ctx.repo.name,
|
|
167642
|
+
headSha: primaryRepoState(ctx.toolState).checkoutSha ?? headSha,
|
|
167643
|
+
conclusion,
|
|
167644
|
+
detailsUrl,
|
|
167645
|
+
reviewUrl: ctx.toolState.approval?.url
|
|
167646
|
+
}).then(() => log.info(`\xBB posted ${RUN_STATUS_CHECK_NAME} check (${conclusion})`)).catch((err) => log.debug(`status checks: ${RUN_STATUS_CHECK_NAME} post failed: ${err}`));
|
|
167647
|
+
}
|
|
167648
|
+
if (!needsApprovalCheck || !approval) return;
|
|
167649
|
+
const createParams = {
|
|
167650
|
+
owner: ctx.repo.owner,
|
|
167651
|
+
repo: ctx.repo.name,
|
|
167652
|
+
name: APPROVAL_CHECK_NAME,
|
|
167653
|
+
head_sha: approval.sha ?? headSha,
|
|
167654
|
+
status: "completed",
|
|
167655
|
+
conclusion: approval.wouldApprove ? "success" : "failure",
|
|
167656
|
+
output: {
|
|
167382
167657
|
title: approval.wouldApprove ? "Pullfrog would approve" : "Pullfrog would not approve",
|
|
167383
167658
|
summary: approval.wouldApprove ? "Pullfrog has no outstanding review feedback on this PR." : "Pullfrog has outstanding review feedback or requested changes on this PR."
|
|
167384
|
-
}
|
|
167385
|
-
}
|
|
167659
|
+
}
|
|
167660
|
+
};
|
|
167661
|
+
if (detailsUrl) createParams.details_url = detailsUrl;
|
|
167662
|
+
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}`));
|
|
167386
167663
|
}
|
|
167387
167664
|
|
|
167388
167665
|
// utils/runLifecycle.ts
|
|
@@ -167825,7 +168102,8 @@ async function main() {
|
|
|
167825
168102
|
model: effectiveModel,
|
|
167826
168103
|
authorized: getAuthorizedModels(),
|
|
167827
168104
|
owner: runContext.repo.owner,
|
|
167828
|
-
name: runContext.repo.name
|
|
168105
|
+
name: runContext.repo.name,
|
|
168106
|
+
secretsUnavailable: runContext.secretsUnavailable
|
|
167829
168107
|
});
|
|
167830
168108
|
}
|
|
167831
168109
|
await setupGit({
|
|
@@ -168322,7 +168600,7 @@ function link(text, url4) {
|
|
|
168322
168600
|
return `\x1B]8;;${url4}\x07${text}\x1B]8;;\x07`;
|
|
168323
168601
|
}
|
|
168324
168602
|
function buildProviders() {
|
|
168325
|
-
return Object.entries(providers).filter(([key]) => key !== "opencode" && key !== "openrouter"
|
|
168603
|
+
return Object.entries(providers).filter(([key]) => key !== "opencode" && key !== "openrouter").map(([key, config3]) => {
|
|
168326
168604
|
const aliases = modelAliases.filter(
|
|
168327
168605
|
(a) => a.provider === key && !a.fallback && !a.routing && !a.hidden
|
|
168328
168606
|
);
|
|
@@ -168342,7 +168620,7 @@ function buildProviders() {
|
|
|
168342
168620
|
hint: a === recommended ? "recommended" : void 0
|
|
168343
168621
|
}))
|
|
168344
168622
|
};
|
|
168345
|
-
});
|
|
168623
|
+
}).filter((p2) => p2.models.length > 0);
|
|
168346
168624
|
}
|
|
168347
168625
|
var CLI_PROVIDERS = buildProviders();
|
|
168348
168626
|
function resolveModelProvider(slug2) {
|
|
@@ -168782,7 +169060,8 @@ async function handleSecret(ctx) {
|
|
|
168782
169060
|
}
|
|
168783
169061
|
if (method === "pullfrog") {
|
|
168784
169062
|
const scope2 = ctx.secrets.isOrg ? await promptScope2(ctx) : "account";
|
|
168785
|
-
|
|
169063
|
+
const target = describeSecretTarget({ owner: ctx.owner, repo: ctx.repo, scope: scope2 });
|
|
169064
|
+
activeSpin2.start(`saving ${import_picocolors3.default.cyan(envVar)} to ${target}`);
|
|
168786
169065
|
let saveResult;
|
|
168787
169066
|
try {
|
|
168788
169067
|
saveResult = await setPullfrogSecret2({
|
|
@@ -168802,7 +169081,7 @@ async function handleSecret(ctx) {
|
|
|
168802
169081
|
return;
|
|
168803
169082
|
}
|
|
168804
169083
|
if (saveResult.saved) {
|
|
168805
|
-
activeSpin2.stop(`saved ${import_picocolors3.default.cyan(envVar)} to
|
|
169084
|
+
activeSpin2.stop(`saved ${import_picocolors3.default.cyan(envVar)} to ${target}`);
|
|
168806
169085
|
} else {
|
|
168807
169086
|
activeSpin2.stop(import_picocolors3.default.red("could not save secret"));
|
|
168808
169087
|
O2.warn(
|
|
@@ -169152,7 +169431,7 @@ async function runCli4(input) {
|
|
|
169152
169431
|
}
|
|
169153
169432
|
|
|
169154
169433
|
// cli.ts
|
|
169155
|
-
var VERSION10 = "0.1.
|
|
169434
|
+
var VERSION10 = "0.1.47";
|
|
169156
169435
|
var bin = basename2(process.argv[1] || "");
|
|
169157
169436
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
169158
169437
|
var rawArgs = process.argv.slice(2);
|