pullfrog 0.1.104 → 0.1.105
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 +3 -3
- package/dist/cli.mjs +290 -177
- package/dist/external.d.ts +3 -2
- package/dist/index.js +122 -44
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal.js +52 -3
- package/dist/models.d.ts +21 -0
- package/dist/utils/agent.d.ts +9 -0
- package/dist/utils/apiKeys.d.ts +2 -0
- package/dist/utils/runEffort.d.ts +10 -0
- package/package.json +1 -1
package/dist/external.d.ts
CHANGED
|
@@ -375,8 +375,9 @@ export interface WriteablePayload {
|
|
|
375
375
|
export type Payload = Readonly<WriteablePayload>;
|
|
376
376
|
/**
|
|
377
377
|
* Whether `find_similar_issues` is registered for this run: an entitled account,
|
|
378
|
-
* on an issue
|
|
379
|
-
* the
|
|
378
|
+
* on an issue or a run with no target (`pullfrog run "enrich issue #42"`), never a PR.
|
|
379
|
+
* Shared by the MCP registration and the prompt so the duplicate-detection
|
|
380
|
+
* instructions can never describe an absent tool.
|
|
380
381
|
*/
|
|
381
382
|
export declare function hasSimilarIssues(params: {
|
|
382
383
|
repoIntelligence: boolean;
|
package/dist/index.js
CHANGED
|
@@ -109939,9 +109939,34 @@ var AZURE_USE_CHAT_COMPLETIONS_ENV = "AZURE_USE_CHAT_COMPLETIONS";
|
|
|
109939
109939
|
var OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
|
|
109940
109940
|
var OPENAI_COMPATIBLE_BASE_URL_ENV = "OPENAI_COMPATIBLE_BASE_URL";
|
|
109941
109941
|
var OPENAI_COMPATIBLE_API_KEY_ENV = "OPENAI_COMPATIBLE_API_KEY";
|
|
109942
|
-
var OPENAI_COMPATIBLE_MODEL_ENV = "OPENAI_COMPATIBLE_MODEL";
|
|
109943
109942
|
var OPENAI_COMPATIBLE_CONTEXT_ENV = "OPENAI_COMPATIBLE_CONTEXT";
|
|
109944
109943
|
var OPENAI_COMPATIBLE_MAX_OUTPUT_ENV = "OPENAI_COMPATIBLE_MAX_OUTPUT";
|
|
109944
|
+
function isOpenAICompatibleSetName(name) {
|
|
109945
|
+
return name !== "byok" && name.length <= 32 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
|
|
109946
|
+
}
|
|
109947
|
+
function openAICompatibleSetName(slug2) {
|
|
109948
|
+
const prefix = `${OPENAI_COMPATIBLE_PROVIDER}/`;
|
|
109949
|
+
if (!slug2.startsWith(prefix)) return void 0;
|
|
109950
|
+
const name = slug2.slice(prefix.length);
|
|
109951
|
+
return isOpenAICompatibleSetName(name) ? name : void 0;
|
|
109952
|
+
}
|
|
109953
|
+
var OPENAI_COMPATIBLE_FIELDS = [
|
|
109954
|
+
"baseUrl",
|
|
109955
|
+
"apiKey",
|
|
109956
|
+
"model",
|
|
109957
|
+
"context",
|
|
109958
|
+
"maxOutput"
|
|
109959
|
+
];
|
|
109960
|
+
function openAICompatibleEnv(name) {
|
|
109961
|
+
const prefix = `OPENAI_COMPATIBLE_${name ? `${name.toUpperCase().replaceAll("-", "_")}_` : ""}`;
|
|
109962
|
+
return {
|
|
109963
|
+
baseUrl: `${prefix}BASE_URL`,
|
|
109964
|
+
apiKey: `${prefix}API_KEY`,
|
|
109965
|
+
model: `${prefix}MODEL`,
|
|
109966
|
+
context: `${prefix}CONTEXT`,
|
|
109967
|
+
maxOutput: `${prefix}MAX_OUTPUT`
|
|
109968
|
+
};
|
|
109969
|
+
}
|
|
109945
109970
|
var PROVIDER_GATEWAY_URL_ENV = {
|
|
109946
109971
|
anthropic: "ANTHROPIC_BASE_URL",
|
|
109947
109972
|
openai: "OPENAI_BASE_URL"
|
|
@@ -109986,7 +110011,7 @@ function findDanglingPromptToolRefs(params) {
|
|
|
109986
110011
|
return [...new Set(extractMcpToolRefs(params.agentId, params.prompt))].filter((name) => !registered.has(name)).sort();
|
|
109987
110012
|
}
|
|
109988
110013
|
function hasSimilarIssues(params) {
|
|
109989
|
-
return params.repoIntelligence &&
|
|
110014
|
+
return params.repoIntelligence && !params.event.is_pr;
|
|
109990
110015
|
}
|
|
109991
110016
|
|
|
109992
110017
|
// utils/activity.ts
|
|
@@ -112115,7 +112140,11 @@ function resolveRunEffort(ctx) {
|
|
|
112115
112140
|
const configured = ctx.payload.effort !== void 0;
|
|
112116
112141
|
const position = ctx.payload.effort ?? DEFAULT_EFFORT_POSITION;
|
|
112117
112142
|
const alias = resolveRunAlias(ctx);
|
|
112118
|
-
if (!alias)
|
|
112143
|
+
if (!alias) {
|
|
112144
|
+
const declarable = configured && !ctx.resolvedModel?.startsWith(`${AZURE_PROVIDER}/`);
|
|
112145
|
+
const rung = declarable && ctx.declaredVariants ? resolveRung({ position, published: ctx.declaredVariants }) : void 0;
|
|
112146
|
+
return { position, configured, rung, alias: void 0, declarable };
|
|
112147
|
+
}
|
|
112119
112148
|
return {
|
|
112120
112149
|
position,
|
|
112121
112150
|
configured,
|
|
@@ -112124,7 +112153,8 @@ function resolveRunEffort(ctx) {
|
|
|
112124
112153
|
position,
|
|
112125
112154
|
useOpenRouter: !!ctx.payload.proxyModel
|
|
112126
112155
|
}),
|
|
112127
|
-
alias
|
|
112156
|
+
alias,
|
|
112157
|
+
declarable: false
|
|
112128
112158
|
};
|
|
112129
112159
|
}
|
|
112130
112160
|
|
|
@@ -112141,7 +112171,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
112141
112171
|
// package.json
|
|
112142
112172
|
var package_default = {
|
|
112143
112173
|
name: "pullfrog",
|
|
112144
|
-
version: "0.1.
|
|
112174
|
+
version: "0.1.105",
|
|
112145
112175
|
cliContractVersion: "1",
|
|
112146
112176
|
type: "module",
|
|
112147
112177
|
bin: {
|
|
@@ -124270,6 +124300,17 @@ function parseModel2(value2) {
|
|
|
124270
124300
|
if (slash <= 0) return void 0;
|
|
124271
124301
|
return { providerID: value2.slice(0, slash), modelID: value2.slice(slash + 1) };
|
|
124272
124302
|
}
|
|
124303
|
+
async function resolveDeclaredRung(ctx, params) {
|
|
124304
|
+
const resp = await params.client.config.providers();
|
|
124305
|
+
const provider2 = resp.data?.providers.find((p) => p.id === params.model.providerID);
|
|
124306
|
+
const variants = provider2?.models[params.model.modelID]?.variants ?? {};
|
|
124307
|
+
const name = `${params.model.providerID}/${params.model.modelID}`;
|
|
124308
|
+
const rung = resolveRunEffort({ ...ctx, declaredVariants: Object.keys(variants) }).rung;
|
|
124309
|
+
log.info(
|
|
124310
|
+
`\xBB effort: ${rung ? `${rung} (declared by opencode for ${name})` : `not applied \u2014 opencode declares no variants for ${name}`}`
|
|
124311
|
+
);
|
|
124312
|
+
return rung;
|
|
124313
|
+
}
|
|
124273
124314
|
function bootOpencodeServer(params) {
|
|
124274
124315
|
const logLevel = isDebugEnabled() ? "INFO" : "ERROR";
|
|
124275
124316
|
if (logLevel !== "ERROR") log.info(`\xBB opencode log level: ${logLevel} (debug run)`);
|
|
@@ -124924,6 +124965,11 @@ var opencode = agent({
|
|
|
124924
124965
|
log.info(`\xBB opencode session: ${sessionID}`);
|
|
124925
124966
|
const labeler = new SessionLabeler();
|
|
124926
124967
|
labeler.labelFor(sessionID);
|
|
124968
|
+
const sdkModel = parseModel2(model);
|
|
124969
|
+
const variant = effort.declarable && sdkModel ? await resolveDeclaredRung(
|
|
124970
|
+
{ ...ctx, resolvedModel: rawModel },
|
|
124971
|
+
{ client: client2, model: sdkModel }
|
|
124972
|
+
) : effort.rung;
|
|
124927
124973
|
const runnerCtx = {
|
|
124928
124974
|
client: client2,
|
|
124929
124975
|
sessionID,
|
|
@@ -124932,7 +124978,7 @@ var opencode = agent({
|
|
|
124932
124978
|
labeler,
|
|
124933
124979
|
toolState: ctx.toolState,
|
|
124934
124980
|
// `rawModel` folds in the auto-select pick — see the opencode.ts twin.
|
|
124935
|
-
variant
|
|
124981
|
+
variant,
|
|
124936
124982
|
todoTracker: ctx.todoTracker,
|
|
124937
124983
|
onActivityTimeout: ctx.onActivityTimeout,
|
|
124938
124984
|
onTurnRecovered: ctx.onTurnRecovered,
|
|
@@ -124994,7 +125040,6 @@ var opencode = agent({
|
|
|
124994
125040
|
timeoutMs: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
124995
125041
|
abortTurn: () => turnController.abort()
|
|
124996
125042
|
});
|
|
124997
|
-
const sdkModel = parseModel2(model);
|
|
124998
125043
|
let salvagesLeft = 1;
|
|
124999
125044
|
const runTurn = async (text) => {
|
|
125000
125045
|
const attempt = (prompt) => {
|
|
@@ -196384,13 +196429,16 @@ function buildImplementPlanLink(ctx, issueNumber, commentId) {
|
|
|
196384
196429
|
const apiUrl = getApiUrl();
|
|
196385
196430
|
return `[Implement plan \u2794](${apiUrl}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${issueNumber}?action=implement&comment_id=${commentId})`;
|
|
196386
196431
|
}
|
|
196387
|
-
function
|
|
196432
|
+
function runIssueNumber(ctx) {
|
|
196433
|
+
return ctx.payload.event.issue_number ?? primaryRepoState(ctx.toolState).issueNumber;
|
|
196434
|
+
}
|
|
196435
|
+
function buildEnrichmentLinks(ctx, issueNumber) {
|
|
196388
196436
|
const event = ctx.payload.event;
|
|
196389
|
-
|
|
196390
|
-
|
|
196391
|
-
|
|
196437
|
+
const mode = ctx.toolState.selectedMode;
|
|
196438
|
+
const enriching = mode === "Enrich" || event.trigger === "issues_opened" && !ctx.payload.prompt && mode !== "Build";
|
|
196439
|
+
if (issueNumber === void 0 || event.is_pr || !enriching) return [];
|
|
196392
196440
|
return [
|
|
196393
|
-
`[Implement a fix \u2794](${getApiUrl()}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${
|
|
196441
|
+
`[Implement a fix \u2794](${getApiUrl()}/trigger/${ctx.repo.owner}/${ctx.repo.name}/${issueNumber}?action=build)`
|
|
196394
196442
|
];
|
|
196395
196443
|
}
|
|
196396
196444
|
function addFooter(ctx, body) {
|
|
@@ -196426,10 +196474,11 @@ function CreateCommentTool(ctx) {
|
|
|
196426
196474
|
});
|
|
196427
196475
|
ctx.toolState.wasUpdated = true;
|
|
196428
196476
|
log.info(`\xBB created comment ${result.data.id}`);
|
|
196429
|
-
|
|
196477
|
+
const ownTarget = issueNumber === runIssueNumber(ctx);
|
|
196478
|
+
if (ownTarget) {
|
|
196430
196479
|
ctx.toolState.standaloneCommentId = result.data.id;
|
|
196431
196480
|
}
|
|
196432
|
-
const enrichmentLinks =
|
|
196481
|
+
const enrichmentLinks = ownTarget ? buildEnrichmentLinks(ctx, issueNumber) : [];
|
|
196433
196482
|
if (commentType === "Plan" || enrichmentLinks.length > 0) {
|
|
196434
196483
|
if (commentType === "Plan" && result.data.node_id) {
|
|
196435
196484
|
await patchWorkflowRunFields(ctx, { planCommentNodeId: result.data.node_id });
|
|
@@ -196509,9 +196558,9 @@ async function reportProgress(ctx, params) {
|
|
|
196509
196558
|
if (params.liveProgress && !ctx.payload.progressComments) {
|
|
196510
196559
|
return { body, action: "skipped" };
|
|
196511
196560
|
}
|
|
196512
|
-
const issueNumber =
|
|
196561
|
+
const issueNumber = runIssueNumber(ctx);
|
|
196513
196562
|
const isPlanMode = ctx.toolState.selectedMode === "Plan";
|
|
196514
|
-
const enrichmentLinks = params.liveProgress ? [] : buildEnrichmentLinks(ctx);
|
|
196563
|
+
const enrichmentLinks = params.liveProgress ? [] : buildEnrichmentLinks(ctx, issueNumber);
|
|
196515
196564
|
const apiCtx = { octokit: ctx.octokit, owner: ctx.repo.owner, repo: ctx.repo.name };
|
|
196516
196565
|
if (target_plan_comment === true && ctx.toolState.existingPlanCommentId === void 0) {
|
|
196517
196566
|
log.warning("target_plan_comment requested but no existingPlanCommentId in tool state");
|
|
@@ -199522,7 +199571,7 @@ function GetIssueCommentsTool(ctx) {
|
|
|
199522
199571
|
description: "Get all comments for a GitHub issue. Returns all comments including the issue body and all subsequent discussion comments. Example: `get_issue_comments({ issue_number: 1234 })`.",
|
|
199523
199572
|
parameters: GetIssueComments,
|
|
199524
199573
|
execute: execute(async ({ issue_number }) => {
|
|
199525
|
-
primaryRepoState(ctx.toolState).issueNumber
|
|
199574
|
+
primaryRepoState(ctx.toolState).issueNumber ??= issue_number;
|
|
199526
199575
|
const comments = await ctx.octokit.paginate(ctx.octokit.rest.issues.listComments, {
|
|
199527
199576
|
owner: ctx.repo.owner,
|
|
199528
199577
|
repo: ctx.repo.name,
|
|
@@ -199560,7 +199609,7 @@ function GetIssueEventsTool(ctx) {
|
|
|
199560
199609
|
description: "Get timeline events for a GitHub issue that aren't reflected in the current state. Returns cross-references to other issues/PRs and commit references. Note: current labels, assignees, state, and milestone are already available via get_issue.",
|
|
199561
199610
|
parameters: GetIssueEvents,
|
|
199562
199611
|
execute: execute(async ({ issue_number }) => {
|
|
199563
|
-
primaryRepoState(ctx.toolState).issueNumber
|
|
199612
|
+
primaryRepoState(ctx.toolState).issueNumber ??= issue_number;
|
|
199564
199613
|
const events = await ctx.octokit.paginate(ctx.octokit.rest.issues.listEventsForTimeline, {
|
|
199565
199614
|
owner: ctx.repo.owner,
|
|
199566
199615
|
repo: ctx.repo.name,
|
|
@@ -199644,7 +199693,7 @@ function IssueInfoTool(ctx) {
|
|
|
199644
199693
|
tmpdir: ctx.tmpdir,
|
|
199645
199694
|
githubToken: ctx.githubInstallationToken
|
|
199646
199695
|
});
|
|
199647
|
-
primaryRepoState(ctx.toolState).issueNumber
|
|
199696
|
+
primaryRepoState(ctx.toolState).issueNumber ??= issue_number;
|
|
199648
199697
|
const hints = [];
|
|
199649
199698
|
if (data.comments > 0) {
|
|
199650
199699
|
hints.push("use get_issue_comments to retrieve all comments for this issue");
|
|
@@ -201112,8 +201161,11 @@ function SimilarIssuesTool(ctx) {
|
|
|
201112
201161
|
mutates: true,
|
|
201113
201162
|
parameters: SimilarIssues,
|
|
201114
201163
|
execute: execute(async (input2) => {
|
|
201115
|
-
|
|
201116
|
-
|
|
201164
|
+
const repoState = primaryRepoState(ctx.toolState);
|
|
201165
|
+
repoState.issueNumber ??= input2.issue_number;
|
|
201166
|
+
const subject = ctx.payload.event.issue_number ?? repoState.issueNumber;
|
|
201167
|
+
if (ctx.payload.event.is_pr || subject !== input2.issue_number) {
|
|
201168
|
+
throw new Error(`find_similar_issues is limited to this run's issue, #${subject}`);
|
|
201117
201169
|
}
|
|
201118
201170
|
const response = await apiFetch({
|
|
201119
201171
|
path: `/api/repo/${ctx.repo.owner}/${ctx.repo.name}/issues/${input2.issue_number}/similar`,
|
|
@@ -201395,7 +201447,7 @@ function buildCommonTools(ctx, outputSchema) {
|
|
|
201395
201447
|
if (hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event })) {
|
|
201396
201448
|
tools.push(SimilarIssuesTool(ctx));
|
|
201397
201449
|
}
|
|
201398
|
-
if (
|
|
201450
|
+
if (!ctx.payload.event.is_pr) {
|
|
201399
201451
|
tools.push(SearchIssuesTool(ctx));
|
|
201400
201452
|
}
|
|
201401
201453
|
const isStandalone = ctx.payload.event.trigger === "unknown";
|
|
@@ -201793,11 +201845,11 @@ function buildOpenAICompatibleSetupError(params) {
|
|
|
201793
201845
|
missing: params.missing,
|
|
201794
201846
|
where: `Set them on the OpenAI-compatible provider in [Pullfrog's provider settings](${getApiUrl()}/console/${params.owner}#billing), or in the workflow's \`env:\` block with the key as an [Actions secret](${secretsUrl(params)}):`,
|
|
201795
201847
|
env: [
|
|
201796
|
-
`${
|
|
201797
|
-
`${
|
|
201798
|
-
`${
|
|
201799
|
-
`${
|
|
201800
|
-
`${
|
|
201848
|
+
`${params.env.baseUrl}: https://your-endpoint.example.com/v1`,
|
|
201849
|
+
`${params.env.apiKey}: \${{ secrets.${params.env.apiKey} }}`,
|
|
201850
|
+
`${params.env.model}: <model-id>`,
|
|
201851
|
+
`${params.env.context}: "128000"`,
|
|
201852
|
+
`${params.env.maxOutput}: "16384"`
|
|
201801
201853
|
],
|
|
201802
201854
|
note: "Use the real context and output limits of the model your endpoint serves: Pullfrog can't discover them, and without them long runs grow until the endpoint refuses them.",
|
|
201803
201855
|
guide: "https://docs.pullfrog.com/openai-compatible"
|
|
@@ -201831,17 +201883,16 @@ function hasPositiveNumberEnvVar(name) {
|
|
|
201831
201883
|
return Number(process.env[name]) > 0;
|
|
201832
201884
|
}
|
|
201833
201885
|
function validateOpenAICompatibleSetup(params) {
|
|
201886
|
+
const env2 = openAICompatibleEnv(params.set);
|
|
201834
201887
|
const missing = [];
|
|
201835
|
-
if (!hasEnvVar2(
|
|
201836
|
-
if (!hasEnvVar2(
|
|
201837
|
-
if (!hasEnvVar2(
|
|
201838
|
-
if (!hasPositiveNumberEnvVar(
|
|
201839
|
-
|
|
201840
|
-
if (!hasPositiveNumberEnvVar(OPENAI_COMPATIBLE_MAX_OUTPUT_ENV))
|
|
201841
|
-
missing.push(OPENAI_COMPATIBLE_MAX_OUTPUT_ENV);
|
|
201888
|
+
if (!hasEnvVar2(env2.baseUrl)) missing.push(env2.baseUrl);
|
|
201889
|
+
if (!hasEnvVar2(env2.apiKey)) missing.push(env2.apiKey);
|
|
201890
|
+
if (!hasEnvVar2(env2.model)) missing.push(env2.model);
|
|
201891
|
+
if (!hasPositiveNumberEnvVar(env2.context)) missing.push(env2.context);
|
|
201892
|
+
if (!hasPositiveNumberEnvVar(env2.maxOutput)) missing.push(env2.maxOutput);
|
|
201842
201893
|
if (missing.length > 0) {
|
|
201843
201894
|
throw new Error(
|
|
201844
|
-
buildOpenAICompatibleSetupError({ owner: params.owner, name: params.name, missing })
|
|
201895
|
+
buildOpenAICompatibleSetupError({ owner: params.owner, name: params.name, missing, env: env2 })
|
|
201845
201896
|
);
|
|
201846
201897
|
}
|
|
201847
201898
|
}
|
|
@@ -201897,7 +201948,11 @@ function validateAgentApiKey(params) {
|
|
|
201897
201948
|
return;
|
|
201898
201949
|
}
|
|
201899
201950
|
if (params.model.startsWith(`${OPENAI_COMPATIBLE_PROVIDER}/`)) {
|
|
201900
|
-
validateOpenAICompatibleSetup({
|
|
201951
|
+
validateOpenAICompatibleSetup({
|
|
201952
|
+
owner: params.owner,
|
|
201953
|
+
name: params.name,
|
|
201954
|
+
set: params.openAICompatibleSet
|
|
201955
|
+
});
|
|
201901
201956
|
return;
|
|
201902
201957
|
}
|
|
201903
201958
|
if (params.model.startsWith(`${AZURE_PROVIDER}/`)) {
|
|
@@ -202211,17 +202266,37 @@ function resolveSlug(slug2) {
|
|
|
202211
202266
|
}
|
|
202212
202267
|
return `${AZURE_PROVIDER}/${deployment}`;
|
|
202213
202268
|
}
|
|
202214
|
-
|
|
202215
|
-
|
|
202269
|
+
const set2 = heldOpenAICompatibleSet(slug2);
|
|
202270
|
+
if (alias?.routing === "openai-compatible" || set2) {
|
|
202271
|
+
const modelEnv = openAICompatibleEnv(set2).model;
|
|
202272
|
+
const modelId = process.env[modelEnv]?.trim();
|
|
202216
202273
|
if (!modelId) {
|
|
202217
202274
|
throw new Error(
|
|
202218
|
-
`\`${
|
|
202275
|
+
`\`${modelEnv}\` ${BYOK_SLUG_MARKER} \`${slug2}\`. Set it to the model ID your endpoint serves, on the OpenAI-compatible provider in Pullfrog or in the workflow's \`env:\` block. [OpenAI-compatible setup guide](https://docs.pullfrog.com/openai-compatible)`
|
|
202219
202276
|
);
|
|
202220
202277
|
}
|
|
202221
202278
|
return `${OPENAI_COMPATIBLE_PROVIDER}/${modelId}`;
|
|
202222
202279
|
}
|
|
202223
202280
|
return resolveCliModel(slug2);
|
|
202224
202281
|
}
|
|
202282
|
+
function heldOpenAICompatibleSet(slug2) {
|
|
202283
|
+
const set2 = openAICompatibleSetName(slug2);
|
|
202284
|
+
return set2 && Object.values(openAICompatibleEnv(set2)).some(hasEnvVar3) ? set2 : void 0;
|
|
202285
|
+
}
|
|
202286
|
+
function activateOpenAICompatibleSet(ctx) {
|
|
202287
|
+
const slug2 = process.env.PULLFROG_MODEL?.trim() || ctx.slug?.trim();
|
|
202288
|
+
const set2 = slug2 ? heldOpenAICompatibleSet(slug2) : void 0;
|
|
202289
|
+
if (!set2) return void 0;
|
|
202290
|
+
const from = openAICompatibleEnv(set2);
|
|
202291
|
+
const to = openAICompatibleEnv();
|
|
202292
|
+
for (const field of OPENAI_COMPATIBLE_FIELDS) {
|
|
202293
|
+
const value2 = process.env[from[field]];
|
|
202294
|
+
if (value2 === void 0) delete process.env[to[field]];
|
|
202295
|
+
else process.env[to[field]] = value2;
|
|
202296
|
+
}
|
|
202297
|
+
log.info(`\xBB using the OpenAI-compatible endpoint "${set2}"`);
|
|
202298
|
+
return set2;
|
|
202299
|
+
}
|
|
202225
202300
|
function resolveModel(ctx) {
|
|
202226
202301
|
const envModel = process.env.PULLFROG_MODEL?.trim();
|
|
202227
202302
|
if (envModel) {
|
|
@@ -203264,7 +203339,7 @@ Rules:
|
|
|
203264
203339
|
### GitHub
|
|
203265
203340
|
|
|
203266
203341
|
Use MCP tools from ${pullfrogMcpName} for all GitHub operations. Never use the \`gh\` CLI \u2014 it is not authenticated and will fail. The MCP tools handle authentication and enforce permissions.
|
|
203267
|
-
${hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event }) ? `
|
|
203342
|
+
${ctx.payload.event.issue_number !== void 0 && hasSimilarIssues({ repoIntelligence: ctx.repoIntelligence, event: ctx.payload.event }) ? `
|
|
203268
203343
|
#### Duplicate detection (enabled for this repository)
|
|
203269
203344
|
|
|
203270
203345
|
Call \`${t("find_similar_issues")}\` for #${ctx.payload.event.issue_number} before planning. If it duplicates an existing issue, link that instead of producing a plan; never close or label on similarity alone.
|
|
@@ -205143,6 +205218,9 @@ function resolveModelForLog(ctx) {
|
|
|
205143
205218
|
function resolveEffortForLog(ctx) {
|
|
205144
205219
|
const effort = resolveRunEffort(ctx);
|
|
205145
205220
|
if (!ctx.resolvedModel && !ctx.payload.proxyModel) return "pending \u2014 model not chosen yet";
|
|
205221
|
+
if (effort.declarable && ctx.agentName === "opencode") {
|
|
205222
|
+
return "pending \u2014 reading the variants opencode declares";
|
|
205223
|
+
}
|
|
205146
205224
|
if (!effort.alias) return "not applied \u2014 model not recognized";
|
|
205147
205225
|
if (!effort.rung) return "n/a (model has no effort control)";
|
|
205148
205226
|
return effort.configured ? effort.rung : `${effort.rung} (default)`;
|
|
@@ -205161,9 +205239,7 @@ function logRunStartup(ctx) {
|
|
|
205161
205239
|
log.info(
|
|
205162
205240
|
`\xBB model: ${resolveModelForLog({ payload: ctx.payload, resolvedModel: ctx.resolvedModel })}`
|
|
205163
205241
|
);
|
|
205164
|
-
log.info(
|
|
205165
|
-
`\xBB effort: ${resolveEffortForLog({ payload: ctx.payload, resolvedModel: ctx.resolvedModel })}`
|
|
205166
|
-
);
|
|
205242
|
+
log.info(`\xBB effort: ${resolveEffortForLog(ctx)}`);
|
|
205167
205243
|
const routing = ctx.payload.routing;
|
|
205168
205244
|
if (routing) {
|
|
205169
205245
|
log.info(
|
|
@@ -205434,6 +205510,7 @@ async function main() {
|
|
|
205434
205510
|
const count = Object.keys(runContext.dbSecrets).length;
|
|
205435
205511
|
if (count > 0) log.info(`\xBB ${count} db secret(s) loaded`);
|
|
205436
205512
|
}
|
|
205513
|
+
const openAICompatibleSet = activateOpenAICompatibleSet({ slug: payload.model });
|
|
205437
205514
|
await initializeCredentialPool({ ctx: runContext, slug: payload.model, injected });
|
|
205438
205515
|
installCodexAuth();
|
|
205439
205516
|
installXaiAuth();
|
|
@@ -205582,7 +205659,8 @@ async function main() {
|
|
|
205582
205659
|
owner: runContext.repo.owner,
|
|
205583
205660
|
name: runContext.repo.name,
|
|
205584
205661
|
secretsUnavailable: runContext.secretsUnavailable,
|
|
205585
|
-
routerUnfunded: runContext.routerUnfunded
|
|
205662
|
+
routerUnfunded: runContext.routerUnfunded,
|
|
205663
|
+
openAICompatibleSet
|
|
205586
205664
|
});
|
|
205587
205665
|
} catch (missingKey) {
|
|
205588
205666
|
const funded = runContext.trialFallback && missingKey instanceof NoUsableCredentialError ? await resolveTrialFallback({
|
package/dist/internal/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type { ConfigField, ConfigScope, ConfigValue } from "../configuration.ts"
|
|
|
7
7
|
export { configEntrySchema, configFields, configResponseSchema, configValueSchema, } from "../configuration.ts";
|
|
8
8
|
export type { AuthorPermission, AutoTier, EffortPosition, ModelAlias, ModelProvider, Payload, PayloadEvent, PayloadRouting, ProviderConfig, PushPermission, RouterSource, RouterTier, ShellPermission, ToolPermission, WriteablePayload, XrepoConfig, } from "../external.ts";
|
|
9
9
|
export { AUTO_EFFICIENT, AUTO_INTELLIGENT, AUTO_ROUTER, autoLadder, autoRoutedSlug, DEFAULT_EFFORT_POSITION, DEFAULT_PROXY_MODEL, defaultAutoTier, EFFORT_ALIASES, getAutoSelectHintModel, getModelEffortLevels, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, isAutoRouted, isAutoSlug, isAutoTier, isCardGatedModel, isEffortPosition, isOssAllowedModel, isRouterTier, ladderRungs, modelAliases, modelHasStoredAuth, modelIdLabel, OSS_MODEL_ALLOWLIST, OSS_RECOMMENDED_MODEL, offeredRungs, ossFundedModelNames, PROVIDER_GATEWAY_URL_ENV, parseEffortPosition, parseModel, providers, pullfrogMcpName, ROUTED_PROVIDERS, ROUTER_LADDER, ROUTER_TIERS, resolveAutoTier, resolveCliModel, resolveDisplayAlias, resolveModelRung, resolveModelSlug, resolveOpenRouterModel, resolvePickModel, resolveRoutedModel, resolveRung, routerProxyOptOut, rungLabel, rungPosition, SUBSIDY_RUNG, TRIAL_FUNDED_USD_LIMIT, } from "../external.ts";
|
|
10
|
+
export { isOpenAICompatibleSetName, openAICompatibleEnv, openAICompatibleSetAlias, openAICompatibleSetName, openAICompatibleSetOf, openAICompatibleSets, } from "../models.ts";
|
|
10
11
|
export type { Mode } from "../modes.ts";
|
|
11
12
|
export { modes } from "../modes.ts";
|
|
12
13
|
export { type CommercialRefusal, commercialPaywallBody } from "../utils/billingErrors.ts";
|
package/dist/internal.js
CHANGED
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "pullfrog",
|
|
11
|
-
version: "0.1.
|
|
11
|
+
version: "0.1.105",
|
|
12
12
|
cliContractVersion: "1",
|
|
13
13
|
type: "module",
|
|
14
14
|
bin: {
|
|
@@ -20079,7 +20079,7 @@ var repoConfigFields = [
|
|
|
20079
20079
|
key: "address-reviews.enabled",
|
|
20080
20080
|
field: "codingAddressReviews",
|
|
20081
20081
|
kind: "toggle",
|
|
20082
|
-
description: "address
|
|
20082
|
+
description: "address reviews on Pullfrog-authored PRs"
|
|
20083
20083
|
},
|
|
20084
20084
|
{
|
|
20085
20085
|
key: "fix-ci.own-prs",
|
|
@@ -21651,7 +21651,10 @@ function modelHasStoredAuth(params) {
|
|
|
21651
21651
|
const authVars = [
|
|
21652
21652
|
...getModelEnvVars(slug),
|
|
21653
21653
|
...getModelManagedCredentials(slug),
|
|
21654
|
-
...HARNESS_ONLY_CREDENTIALS[getModelProvider(slug)] ?? []
|
|
21654
|
+
...HARNESS_ONLY_CREDENTIALS[getModelProvider(slug)] ?? [],
|
|
21655
|
+
// the default endpoint's vars above still count: a runner holding no such set runs the
|
|
21656
|
+
// slug as a raw model id on it
|
|
21657
|
+
...openAICompatibleSetEnvVars(slug)
|
|
21655
21658
|
];
|
|
21656
21659
|
return authVars.some((v) => params.secretNames.includes(v));
|
|
21657
21660
|
}
|
|
@@ -21877,6 +21880,46 @@ function isOssAllowedModel(slug) {
|
|
|
21877
21880
|
function getAutoSelectHintModel() {
|
|
21878
21881
|
return defaultProxyDisplayName;
|
|
21879
21882
|
}
|
|
21883
|
+
var OPENAI_COMPATIBLE_PROVIDER = "openai-compatible";
|
|
21884
|
+
function isOpenAICompatibleSetName(name) {
|
|
21885
|
+
return name !== "byok" && name.length <= 32 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
|
|
21886
|
+
}
|
|
21887
|
+
function openAICompatibleSetName(slug) {
|
|
21888
|
+
const prefix = `${OPENAI_COMPATIBLE_PROVIDER}/`;
|
|
21889
|
+
if (!slug.startsWith(prefix)) return void 0;
|
|
21890
|
+
const name = slug.slice(prefix.length);
|
|
21891
|
+
return isOpenAICompatibleSetName(name) ? name : void 0;
|
|
21892
|
+
}
|
|
21893
|
+
function openAICompatibleEnv(name) {
|
|
21894
|
+
const prefix = `OPENAI_COMPATIBLE_${name ? `${name.toUpperCase().replaceAll("-", "_")}_` : ""}`;
|
|
21895
|
+
return {
|
|
21896
|
+
baseUrl: `${prefix}BASE_URL`,
|
|
21897
|
+
apiKey: `${prefix}API_KEY`,
|
|
21898
|
+
model: `${prefix}MODEL`,
|
|
21899
|
+
context: `${prefix}CONTEXT`,
|
|
21900
|
+
maxOutput: `${prefix}MAX_OUTPUT`
|
|
21901
|
+
};
|
|
21902
|
+
}
|
|
21903
|
+
function openAICompatibleSetEnvVars(slug) {
|
|
21904
|
+
const set2 = openAICompatibleSetName(slug);
|
|
21905
|
+
if (!set2) return [];
|
|
21906
|
+
const env = openAICompatibleEnv(set2);
|
|
21907
|
+
return [env.baseUrl, env.apiKey, env.model];
|
|
21908
|
+
}
|
|
21909
|
+
function openAICompatibleSetOf(envName) {
|
|
21910
|
+
const match = /^OPENAI_COMPATIBLE_([A-Z0-9]+(?:_[A-Z0-9]+)*)_(?:BASE_URL|API_KEY|MODEL|CONTEXT|MAX_OUTPUT)$/.exec(
|
|
21911
|
+
envName
|
|
21912
|
+
);
|
|
21913
|
+
const name = match?.[1]?.toLowerCase().replaceAll("_", "-");
|
|
21914
|
+
return name && isOpenAICompatibleSetName(name) ? name : void 0;
|
|
21915
|
+
}
|
|
21916
|
+
function openAICompatibleSets(names) {
|
|
21917
|
+
return [...new Set(names.flatMap((name) => openAICompatibleSetOf(name) ?? []))].sort();
|
|
21918
|
+
}
|
|
21919
|
+
function openAICompatibleSetAlias(set2) {
|
|
21920
|
+
const byok = resolveDisplayAlias(`${OPENAI_COMPATIBLE_PROVIDER}/byok`);
|
|
21921
|
+
return byok && { ...byok, slug: `${OPENAI_COMPATIBLE_PROVIDER}/${set2}`, displayName: set2 };
|
|
21922
|
+
}
|
|
21880
21923
|
var PROVIDER_GATEWAY_URL_ENV = {
|
|
21881
21924
|
anthropic: "ANTHROPIC_BASE_URL",
|
|
21882
21925
|
openai: "OPENAI_BASE_URL"
|
|
@@ -23534,6 +23577,7 @@ export {
|
|
|
23534
23577
|
isEffortPosition,
|
|
23535
23578
|
isLeapingIntoActionCommentBody,
|
|
23536
23579
|
isLiveChecklistCommentBody,
|
|
23580
|
+
isOpenAICompatibleSetName,
|
|
23537
23581
|
isOssAllowedModel,
|
|
23538
23582
|
isPullfrog,
|
|
23539
23583
|
isRouterTier,
|
|
@@ -23544,6 +23588,11 @@ export {
|
|
|
23544
23588
|
modelIdLabel,
|
|
23545
23589
|
modes,
|
|
23546
23590
|
offeredRungs,
|
|
23591
|
+
openAICompatibleEnv,
|
|
23592
|
+
openAICompatibleSetAlias,
|
|
23593
|
+
openAICompatibleSetName,
|
|
23594
|
+
openAICompatibleSetOf,
|
|
23595
|
+
openAICompatibleSets,
|
|
23547
23596
|
ossFundedModelNames,
|
|
23548
23597
|
parseCodexAuthBody,
|
|
23549
23598
|
parseEffortPosition,
|
package/dist/models.d.ts
CHANGED
|
@@ -464,6 +464,27 @@ export declare const OPENAI_COMPATIBLE_CONTEXT_ENV = "OPENAI_COMPATIBLE_CONTEXT"
|
|
|
464
464
|
* `context` + `output` together, so the pair is validated and emitted as a unit.
|
|
465
465
|
*/
|
|
466
466
|
export declare const OPENAI_COMPATIBLE_MAX_OUTPUT_ENV = "OPENAI_COMPATIBLE_MAX_OUTPUT";
|
|
467
|
+
/**
|
|
468
|
+
* a NAMED OpenAI-compatible endpoint: the slug `openai-compatible/<name>` runs on the five
|
|
469
|
+
* `OPENAI_COMPATIBLE_<NAME>_*` values, so one account holds several endpoints and each repo,
|
|
470
|
+
* trigger or `--model=` picks one. `byok` stays the unnamed default on the bare names. 32 is
|
|
471
|
+
* what keeps `OPENAI_COMPATIBLE_<NAME>_MAX_OUTPUT` under the 64-character secret-name cap.
|
|
472
|
+
*/
|
|
473
|
+
export declare function isOpenAICompatibleSetName(name: string): boolean;
|
|
474
|
+
/**
|
|
475
|
+
* the set a slug names, by shape alone. `openai-compatible/<x>` is also a raw specifier (model
|
|
476
|
+
* `x` on the default endpoint), so a runner treats it as a set only when it holds that set's values.
|
|
477
|
+
*/
|
|
478
|
+
export declare function openAICompatibleSetName(slug: string): string | undefined;
|
|
479
|
+
export declare const OPENAI_COMPATIBLE_FIELDS: readonly ["baseUrl", "apiKey", "model", "context", "maxOutput"];
|
|
480
|
+
/** the env var names one endpoint is configured by: a named set's, or the default's. */
|
|
481
|
+
export declare function openAICompatibleEnv(name?: string): Record<(typeof OPENAI_COMPATIBLE_FIELDS)[number], string>;
|
|
482
|
+
/** the set a variable belongs to: `OPENAI_COMPATIBLE_VLLM_A_API_KEY` → `vllm-a`. */
|
|
483
|
+
export declare function openAICompatibleSetOf(envName: string): string | undefined;
|
|
484
|
+
/** the named endpoints `names` (stored secret or env var names) hold any value of. */
|
|
485
|
+
export declare function openAICompatibleSets(names: string[]): string[];
|
|
486
|
+
/** a named endpoint as a picker entry: the default endpoint's routing alias under its own slug. */
|
|
487
|
+
export declare function openAICompatibleSetAlias(set: string): ModelAlias | undefined;
|
|
467
488
|
/**
|
|
468
489
|
* Providers whose endpoint can be re-pointed at a customer gateway, and the env
|
|
469
490
|
* var that does it. Deliberately a short explicit map rather than a derived
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { Agent } from "../agents/index.ts";
|
|
2
|
+
/**
|
|
3
|
+
* make the run's named set (`openai-compatible/<name>`) its endpoint: the set's five values
|
|
4
|
+
* replace the bare `OPENAI_COMPATIBLE_*` that validation and the opencode provider read. a value
|
|
5
|
+
* the set lacks is cleared, never inherited, so the default endpoint's key cannot reach the set's
|
|
6
|
+
* URL. returns the set, so setup errors can name its own variables.
|
|
7
|
+
*/
|
|
8
|
+
export declare function activateOpenAICompatibleSet(ctx: {
|
|
9
|
+
slug: string | undefined;
|
|
10
|
+
}): string | undefined;
|
|
2
11
|
/**
|
|
3
12
|
* resolve the effective model for this run.
|
|
4
13
|
*
|
package/dist/utils/apiKeys.d.ts
CHANGED
|
@@ -121,6 +121,8 @@ export declare function validateAgentApiKey(params: {
|
|
|
121
121
|
* says nothing about what the user actually configured. */
|
|
122
122
|
secretsUnavailable?: boolean | undefined;
|
|
123
123
|
routerUnfunded?: boolean | undefined;
|
|
124
|
+
/** the named OpenAI-compatible set the run is on, from `activateOpenAICompatibleSet`. */
|
|
125
|
+
openAICompatibleSet?: string | undefined;
|
|
124
126
|
}): void;
|
|
125
127
|
/**
|
|
126
128
|
* Detect agent-runtime auth failures that should be reformatted as an actionable
|
|
@@ -20,6 +20,11 @@ export interface RunEffort {
|
|
|
20
20
|
rung: string | undefined;
|
|
21
21
|
/** the catalog alias the ladder came from, if we recognized one. */
|
|
22
22
|
alias: ModelAlias | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* true when the catalog has no ladder for a configured effort and the variants
|
|
25
|
+
* the opencode server declares for the model may stand in (`declaredVariants`).
|
|
26
|
+
*/
|
|
27
|
+
declarable: boolean;
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
30
|
* the position a subsidised run is pinned to — the `high` rung on the ladder of
|
|
@@ -39,4 +44,9 @@ export declare function ossEffortFloor(ctx: {
|
|
|
39
44
|
export declare function resolveRunEffort(ctx: {
|
|
40
45
|
payload: ResolvedPayload;
|
|
41
46
|
resolvedModel?: string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* the variant names the opencode server lists for the running model, in its
|
|
49
|
+
* order. only consulted when the catalog has no alias for the model.
|
|
50
|
+
*/
|
|
51
|
+
declaredVariants?: readonly string[] | undefined;
|
|
42
52
|
}): RunEffort;
|