pullfrog 0.1.55 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +678 -517
- package/dist/external.d.ts +28 -0
- package/dist/index.js +677 -516
- package/dist/internal.js +30 -13
- package/dist/mcp/currentObject.d.ts +14 -0
- package/dist/mcp/gh.d.ts +32 -0
- package/dist/mcp/issue.d.ts +0 -23
- package/dist/mcp/pr.d.ts +0 -10
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/shell.d.ts +24 -0
- package/dist/utils/billingErrors.d.ts +9 -1
- package/dist/utils/github.d.ts +1 -1
- package/dist/utils/roleMirror.d.ts +42 -0
- package/dist/utils/token.d.ts +3 -1
- package/dist/utils/worktree.d.ts +14 -2
- package/package.json +1 -1
package/dist/internal.js
CHANGED
|
@@ -247,12 +247,22 @@ var providers = {
|
|
|
247
247
|
displayName: "DeepSeek",
|
|
248
248
|
envVars: ["DEEPSEEK_API_KEY"],
|
|
249
249
|
models: {
|
|
250
|
+
// same fork trap as `deepseek-flash` below, and Pro fell into it on
|
|
251
|
+
// 2026-08-12: OpenRouter forked the 0813 release into its own id and left
|
|
252
|
+
// the April preview live under the unversioned one at 2.7x the input and
|
|
253
|
+
// 27x the CACHE-READ rate ($1.168/$2.336/$0.09855 vs
|
|
254
|
+
// $0.435/$0.87/$0.003625). the direct route upgraded in place, so only the
|
|
255
|
+
// OpenRouter side was stale — and that side is what OSS and Router runs
|
|
256
|
+
// use. measured: $0.1719/run authoritative against the funded default's
|
|
257
|
+
// $0.0405. there is no `~deepseek/*-pro-latest` pointer to track, so this
|
|
258
|
+
// one has to be a dated pin; the `models-catalog` drift test is what
|
|
259
|
+
// catches the next release.
|
|
250
260
|
"deepseek-pro": {
|
|
251
261
|
displayName: "DeepSeek Pro",
|
|
252
262
|
resolve: "deepseek/deepseek-v4-pro",
|
|
253
263
|
effort: ["high", "max"],
|
|
254
|
-
openRouterEffort: ["high", "
|
|
255
|
-
openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
|
|
264
|
+
openRouterEffort: ["low", "high", "max"],
|
|
265
|
+
openRouterResolve: "openrouter/deepseek/deepseek-v4-pro-0813",
|
|
256
266
|
preferred: true
|
|
257
267
|
},
|
|
258
268
|
// DeepSeek upgrades the `deepseek-v4-flash` API model in place, so the
|
|
@@ -260,14 +270,19 @@ var providers = {
|
|
|
260
270
|
// release into its own id and left the April preview live under the
|
|
261
271
|
// unversioned one at a HIGHER price ($0.14/$0.28 vs $0.09/$0.18) — so the
|
|
262
272
|
// OpenRouter route can never use the bare id. it tracks the rolling
|
|
263
|
-
// `~deepseek/*-latest` pointer (rule 4), which
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
273
|
+
// `~deepseek/*-latest` pointer (rule 4), which follows the next in-place
|
|
274
|
+
// upgrade on its own rather than pinning a dated snapshot that goes stale
|
|
275
|
+
// every release. that pointer is NOT priced like 0731 — its listed rates
|
|
276
|
+
// run +40% on output and +57.5% on cache read, and it costs 12% more per
|
|
277
|
+
// run on authoritative spend ($0.0405 vs $0.0361). kept anyway: ~$45/yr
|
|
278
|
+
// buys auto-following the upgrade, and the listed gap overstates the real
|
|
279
|
+
// one because a rolling alias prices at its routing ceiling.
|
|
280
|
+
// 0731 gained a `low` rung and renamed the top to `max`, and that ladder
|
|
281
|
+
// now reads the same on BOTH routes — the in-place direct upgrade caught
|
|
282
|
+
// up to the fork. so `low`, not `high`, is position 0 either way, which is
|
|
283
|
+
// why the OSS effort floor in `main.ts` pins the `high` rung by name. Pro
|
|
284
|
+
// is the same shape via OpenRouter since 0813; only its direct route still
|
|
285
|
+
// starts at `high`.
|
|
271
286
|
"deepseek-flash": {
|
|
272
287
|
displayName: "DeepSeek Flash",
|
|
273
288
|
resolve: "deepseek/deepseek-v4-flash",
|
|
@@ -666,11 +681,13 @@ var providers = {
|
|
|
666
681
|
effort: ["low", "medium", "high"],
|
|
667
682
|
openRouterResolve: "openrouter/x-ai/grok-4.5"
|
|
668
683
|
},
|
|
684
|
+
// dated pin, not the bare id — see `deepseek/deepseek-pro` for why
|
|
685
|
+
// OpenRouter's unversioned `deepseek-v4-pro` is the stale April preview.
|
|
669
686
|
"deepseek-pro": {
|
|
670
687
|
displayName: "DeepSeek Pro",
|
|
671
|
-
resolve: "openrouter/deepseek/deepseek-v4-pro",
|
|
672
|
-
effort: ["high", "
|
|
673
|
-
openRouterResolve: "openrouter/deepseek/deepseek-v4-pro"
|
|
688
|
+
resolve: "openrouter/deepseek/deepseek-v4-pro-0813",
|
|
689
|
+
effort: ["low", "high", "max"],
|
|
690
|
+
openRouterResolve: "openrouter/deepseek/deepseek-v4-pro-0813"
|
|
674
691
|
},
|
|
675
692
|
// rolling pointer, not the bare id — see `deepseek/deepseek-flash` for why
|
|
676
693
|
// OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ToolContext } from "./server.ts";
|
|
2
|
+
export declare const CloseCurrent: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
3
|
+
state_reason: "completed" | "not_planned" | "duplicate";
|
|
4
|
+
}, {}>;
|
|
5
|
+
export declare function CloseCurrentTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
6
|
+
state_reason: "completed" | "not_planned" | "duplicate";
|
|
7
|
+
}, {
|
|
8
|
+
state_reason: "completed" | "not_planned" | "duplicate";
|
|
9
|
+
}>> & {
|
|
10
|
+
mutates?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function ReopenCurrentTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<object, object>> & {
|
|
13
|
+
mutates?: boolean;
|
|
14
|
+
};
|
package/dist/mcp/gh.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ToolContext } from "./server.ts";
|
|
2
|
+
export declare const GhParams: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
3
|
+
args: string[];
|
|
4
|
+
}, {}>;
|
|
5
|
+
/**
|
|
6
|
+
* The GitHub CLI, authenticated with a token scoped to mirror the triggering
|
|
7
|
+
* user's own repository role (see action/utils/roleMirror.ts).
|
|
8
|
+
*
|
|
9
|
+
* Registered only when that mirror yields a token — triage and above. Below
|
|
10
|
+
* that the object-scoped tools are registered instead, because no GitHub
|
|
11
|
+
* permission set can express "act only on the object you authored".
|
|
12
|
+
*
|
|
13
|
+
* There is deliberately NO subcommand allowlist. `gh auth token` prints its own
|
|
14
|
+
* credential by design, `GH_DEBUG=api` dumps the Authorization header, and
|
|
15
|
+
* aliases and extensions are general code execution — so containment of the
|
|
16
|
+
* TOKEN is not achievable and is not attempted; correct scoping plus revocation
|
|
17
|
+
* at run end is what makes a leak survivable.
|
|
18
|
+
*
|
|
19
|
+
* That last property is exactly why this runs through `runSandboxed` rather
|
|
20
|
+
* than spawning directly: `gh alias set x '!<shell>'` is arbitrary code
|
|
21
|
+
* execution, and outside the sandbox it would reach pullfrog-managed on-disk
|
|
22
|
+
* secrets, the runner's workflow-command files and a writable `.git` — none of
|
|
23
|
+
* which the token's scope bounds. Inside it, that capability is no more than
|
|
24
|
+
* the `shell` tool already grants. See wiki/agent-github-access.md.
|
|
25
|
+
*/
|
|
26
|
+
export declare function GhTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
27
|
+
args: string[];
|
|
28
|
+
}, {
|
|
29
|
+
args: string[];
|
|
30
|
+
}>> & {
|
|
31
|
+
mutates?: boolean;
|
|
32
|
+
};
|
package/dist/mcp/issue.d.ts
CHANGED
|
@@ -18,26 +18,3 @@ export declare function IssueTool(ctx: ToolContext): import("fastmcp").Tool<any,
|
|
|
18
18
|
}>> & {
|
|
19
19
|
mutates?: boolean;
|
|
20
20
|
};
|
|
21
|
-
export declare const CloseIssue: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
22
|
-
issue_number: number;
|
|
23
|
-
state_reason: "completed" | "not_planned" | "duplicate";
|
|
24
|
-
}, {}>;
|
|
25
|
-
export declare function CloseIssueTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
26
|
-
issue_number: number;
|
|
27
|
-
state_reason: "completed" | "not_planned" | "duplicate";
|
|
28
|
-
}, {
|
|
29
|
-
issue_number: number;
|
|
30
|
-
state_reason: "completed" | "not_planned" | "duplicate";
|
|
31
|
-
}>> & {
|
|
32
|
-
mutates?: boolean;
|
|
33
|
-
};
|
|
34
|
-
export declare const ReopenIssue: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
35
|
-
issue_number: number;
|
|
36
|
-
}, {}>;
|
|
37
|
-
export declare function ReopenIssueTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
38
|
-
issue_number: number;
|
|
39
|
-
}, {
|
|
40
|
-
issue_number: number;
|
|
41
|
-
}>> & {
|
|
42
|
-
mutates?: boolean;
|
|
43
|
-
};
|
package/dist/mcp/pr.d.ts
CHANGED
|
@@ -6,16 +6,6 @@ export declare const PullRequest: import("arktype/internal/variants/object.ts").
|
|
|
6
6
|
draft?: boolean;
|
|
7
7
|
repo?: string;
|
|
8
8
|
}, {}>;
|
|
9
|
-
export declare const ClosePullRequest: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
10
|
-
pull_number: number;
|
|
11
|
-
}, {}>;
|
|
12
|
-
export declare function ClosePullRequestTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
13
|
-
pull_number: number;
|
|
14
|
-
}, {
|
|
15
|
-
pull_number: number;
|
|
16
|
-
}>> & {
|
|
17
|
-
mutates?: boolean;
|
|
18
|
-
};
|
|
19
9
|
export declare const UpdatePullRequestBody: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
20
10
|
pull_number: number;
|
|
21
11
|
body: string;
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface ToolContext {
|
|
|
16
16
|
gitToken: string;
|
|
17
17
|
refreshGitToken: ((stale: string) => Promise<string>) | undefined;
|
|
18
18
|
readToken: string | undefined;
|
|
19
|
+
ghToken: string | undefined;
|
|
19
20
|
xrepo: XrepoConfig | undefined;
|
|
20
21
|
apiToken: string;
|
|
21
22
|
modes: Mode[];
|
|
@@ -50,6 +51,7 @@ type McpHttpServerOptions = {
|
|
|
50
51
|
*/
|
|
51
52
|
export declare function startMcpHttpServer(ctx: ToolContext, options?: McpHttpServerOptions): Promise<{
|
|
52
53
|
url: string;
|
|
54
|
+
toolNames: string[];
|
|
53
55
|
[Symbol.asyncDispose]: () => Promise<void>;
|
|
54
56
|
}>;
|
|
55
57
|
export {};
|
package/dist/mcp/shell.d.ts
CHANGED
|
@@ -14,6 +14,30 @@ export declare function getSandboxMethod(): SandboxMethod;
|
|
|
14
14
|
* runners, build tools, grep on large trees), so the overflow is spilled
|
|
15
15
|
* to a tempfile the agent can re-read selectively (cat/tail/grep). */
|
|
16
16
|
export declare const MAX_OUTPUT_CHARS = 5000;
|
|
17
|
+
/** if `output` exceeds `MAX_OUTPUT_CHARS`, persist the full body to a
|
|
18
|
+
* tempfile and return the last `MAX_OUTPUT_CHARS` prefixed with a sentinel
|
|
19
|
+
* pointing at the saved path. otherwise return as-is. */
|
|
20
|
+
/**
|
|
21
|
+
* Run one command inside the mandatory shell sandbox and collect its output.
|
|
22
|
+
*
|
|
23
|
+
* Shared by `shell` and `gh` so that agent-initiated process execution carries
|
|
24
|
+
* the same mount- and PID-namespace guarantees no matter which tool starts it.
|
|
25
|
+
* A bare `spawnSync` here would escape `PROC_CLEANUP`, `SOCKET_CLEANUP` and
|
|
26
|
+
* `buildFsMounts` — i.e. the tmpfs over `/var/lib/pullfrog` (codex `auth.json`),
|
|
27
|
+
* the tmpfs over `$RUNNER_TEMP/_runner_file_commands`, and the `.git` ro-bind —
|
|
28
|
+
* none of which any token's scope bounds.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runSandboxed(params: {
|
|
31
|
+
command: string;
|
|
32
|
+
env: Record<string, string | undefined>;
|
|
33
|
+
cwd: string;
|
|
34
|
+
timeout: number;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
output: string;
|
|
37
|
+
exitCode: number;
|
|
38
|
+
timedOut: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
export declare function capOutput(output: string): string;
|
|
17
41
|
export declare function ShellTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
18
42
|
command: string;
|
|
19
43
|
description?: string;
|
|
@@ -56,7 +56,15 @@ export declare function commercialPaywallBody(params: {
|
|
|
56
56
|
ownerLogin: string;
|
|
57
57
|
url: string;
|
|
58
58
|
}): string;
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* commercial-gate copy for action runs, linked to the account billing card.
|
|
61
|
+
*
|
|
62
|
+
* if this fires on a `pullfrog/preview-*` repo it is an infrastructure bug, not
|
|
63
|
+
* a billing state — those repos have no real billing state to refuse. the
|
|
64
|
+
* original cause was the gate comparing the subscription's originating host
|
|
65
|
+
* with strict equality, which no preview could satisfy against a cloned
|
|
66
|
+
* production row; see `isSubscriptionHostTrusted` and wiki/billing-model-v2.md.
|
|
67
|
+
*/
|
|
60
68
|
export declare function formatCommercialGateSummary(params: {
|
|
61
69
|
reason: "commercial" | "subscription_unpaid";
|
|
62
70
|
ownerLogin: string;
|
package/dist/utils/github.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ type WriteOnly = "write";
|
|
|
24
24
|
* @see https://docs.github.com/en/rest/apps/installations#create-an-installation-access-token-for-an-app
|
|
25
25
|
* @see https://github.com/github/rest-api-description — components.schemas.app-permissions
|
|
26
26
|
*/
|
|
27
|
-
type GitHubAppPermissions = {
|
|
27
|
+
export type GitHubAppPermissions = {
|
|
28
28
|
actions?: ReadWrite;
|
|
29
29
|
artifact_metadata?: ReadWrite;
|
|
30
30
|
attestations?: ReadWrite;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AuthorPermission } from "../external.ts";
|
|
2
|
+
import type { GitHubAppPermissions } from "./github.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The installation-token permission set that mirrors the triggering user's own
|
|
5
|
+
* repository role, or `null` when the role sits below the threshold at which
|
|
6
|
+
* the agent is handed a GitHub CLI credential at all.
|
|
7
|
+
*
|
|
8
|
+
* The invariant this exists to hold: **a leaked run token must never grant more
|
|
9
|
+
* than the triggering user already has on that repo.** `gh` can print its own
|
|
10
|
+
* credential (`gh auth token`), dump it via `GH_DEBUG=api`, and reach arbitrary
|
|
11
|
+
* code through aliases and extensions, so containment is not achievable and is
|
|
12
|
+
* not attempted — correct scoping plus revocation at run end is what makes a
|
|
13
|
+
* leak survivable. See wiki/agent-github-access.md.
|
|
14
|
+
*
|
|
15
|
+
* The mirror is exact from `triage` up. At `read`/`none` no permission set
|
|
16
|
+
* expresses "comment, and act only on objects you authored", because GitHub
|
|
17
|
+
* permissions have no object granularity — so those runs get no token and use
|
|
18
|
+
* the object-scoped MCP tools instead.
|
|
19
|
+
*
|
|
20
|
+
* The set is a strict SUBSET of what any qualifying role holds, so the mirror
|
|
21
|
+
* is one-directional by construction — it can never over-grant:
|
|
22
|
+
*
|
|
23
|
+
* - `contents` is **never** `write`, at any tier. That is what keeps the repo's
|
|
24
|
+
* `push` setting meaningful (a `push: "disabled"` repo must not receive a
|
|
25
|
+
* write credential by another door) and what keeps `gh pr merge` and
|
|
26
|
+
* `gh api` ref writes structurally unavailable — merging is authorized by
|
|
27
|
+
* `contents:write`, so withholding it is what makes
|
|
28
|
+
* wiki/autonomous-maintenance.md's "no injected instruction can cause a
|
|
29
|
+
* merge" still true. Legitimate pushes go through `push_branch`, which
|
|
30
|
+
* enforces the branch rules.
|
|
31
|
+
* - `checks` is absent for the same class of reason: no human role grants it,
|
|
32
|
+
* and it would let the agent post its own passing `pullfrog-approval` status
|
|
33
|
+
* (see wiki/review-approval.md).
|
|
34
|
+
* - Every qualifying role therefore receives the SAME set. The role decides
|
|
35
|
+
* whether a token is minted at all, not how wide it is — `admin`, `maintain`
|
|
36
|
+
* and `write` could not be told apart anyway, because everything that
|
|
37
|
+
* separates them needs the `administration` permission the app deliberately
|
|
38
|
+
* does not request (see wiki/app-permissions.md).
|
|
39
|
+
*/
|
|
40
|
+
export declare function mirrorRolePermissions(role: AuthorPermission | undefined): GitHubAppPermissions | null;
|
|
41
|
+
/** render a permission set as `contents:write, issues:write` for logs and the prompt. */
|
|
42
|
+
export declare function formatPermissions(permissions: GitHubAppPermissions): string;
|
package/dist/utils/token.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PushPermission, XrepoConfig } from "../external.ts";
|
|
1
|
+
import type { AuthorPermission, PushPermission, XrepoConfig } from "../external.ts";
|
|
2
2
|
import { acquireNewToken, type OidcCredentials } from "./github.ts";
|
|
3
3
|
export { acquireNewToken as acquireInstallationToken };
|
|
4
4
|
export { revokeGitHubInstallationToken as revokeInstallationToken };
|
|
@@ -21,11 +21,13 @@ export type TokenRef = {
|
|
|
21
21
|
gitToken: string;
|
|
22
22
|
mcpToken: string;
|
|
23
23
|
readToken?: string | undefined;
|
|
24
|
+
ghToken?: string | undefined;
|
|
24
25
|
refreshGitToken?: ((stale: string) => Promise<string>) | undefined;
|
|
25
26
|
[Symbol.asyncDispose]: () => Promise<void>;
|
|
26
27
|
};
|
|
27
28
|
type ResolveTokensParams = {
|
|
28
29
|
push: PushPermission;
|
|
30
|
+
authorPermission: AuthorPermission | undefined;
|
|
29
31
|
xrepo?: XrepoConfig | undefined;
|
|
30
32
|
/**
|
|
31
33
|
* OIDC credentials stashed by main.ts before the restricted-mode env wipe —
|
package/dist/utils/worktree.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* tracked paths with staged or unstaged modifications.
|
|
3
|
+
*
|
|
4
|
+
* `cwd` is not optional decoration: the CLI does NOT start inside the checkout.
|
|
5
|
+
* `runCli.ts` launches it from a bootstrap tmpdir (published) or `actionRoot` (a
|
|
6
|
+
* tarball action checkout with no `.git`), and `main.ts` only chdirs into the
|
|
7
|
+
* repo later. A caller that runs before that chdir and relies on `process.cwd()`
|
|
8
|
+
* gets `exit 129 — Not a git repository` and, since this throws, kills the run:
|
|
9
|
+
* that is exactly what 0.1.54 did to every customer. Pass the repo dir.
|
|
10
|
+
*/
|
|
11
|
+
export declare function dirtyTrackedPaths(params?: {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
}): Promise<Set<string>>;
|
|
3
14
|
/**
|
|
4
15
|
* discard tracked-file mods that appeared since the `before` snapshot, so the
|
|
5
16
|
* customer checkout is clean before agent tools run — any dirt makes
|
|
@@ -17,4 +28,5 @@ export declare function dirtyTrackedPaths(): Promise<Set<string>>;
|
|
|
17
28
|
export declare function restoreDirtiedSince(params: {
|
|
18
29
|
before: Set<string>;
|
|
19
30
|
actor: string;
|
|
31
|
+
cwd?: string;
|
|
20
32
|
}): Promise<void>;
|