@sentry/junior-github 0.114.0 → 0.116.0
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/SETUP.md +2 -2
- package/dist/credential-support.d.ts +90 -0
- package/dist/db/schema.d.ts +5 -5
- package/dist/git-config.d.ts +21 -0
- package/dist/index.d.ts +1 -39
- package/dist/index.js +156 -116
- package/dist/outcomes/cost.d.ts +6 -0
- package/dist/permissions.d.ts +1 -3
- package/dist/plugin.d.ts +44 -0
- package/dist/pull-request-outcomes/store.d.ts +1 -1
- package/package.json +2 -2
- package/skills/attach-github-assets/SOURCES.md +2 -1
package/SETUP.md
CHANGED
|
@@ -116,11 +116,11 @@ githubPlugin({
|
|
|
116
116
|
});
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
Installation-read token requests remain read-only by requesting read-capable configured permissions at `read` level and omitting GitHub permission fields that have no `read` value. Installation-write token requests intentionally omit the `permissions` field, so GitHub applies the complete permission envelope approved on the App installation. GitHub remains the source of truth for whether a permission name or level exists.
|
|
120
120
|
|
|
121
121
|
GitHub App user-to-server tokens do not use OAuth scopes as their permission model. Their effective access is limited by the GitHub App's installed permissions, the app installation's repository access, and the requesting user's own GitHub access. Repository-scoped installation tokens instead use the App permission envelope and installation repository access without borrowing the requesting user's authority. GitHub returns an empty `scope` value for user-to-server tokens, so Junior cannot verify granted scopes from the token response.
|
|
122
122
|
|
|
123
|
-
If you pass `additionalUserScopes`, Junior includes those values in the authorization URL and records the requested scope string as a local reauthorization contract. This does not expand or prove GitHub API permissions. Configure provider-enforced access in the GitHub App settings; `appPermissions` only
|
|
123
|
+
If you pass `additionalUserScopes`, Junior includes those values in the authorization URL and records the requested scope string as a local reauthorization contract. This does not expand or prove GitHub API permissions. Configure provider-enforced access in the GitHub App settings; `appPermissions` only controls read-token downscoping:
|
|
124
124
|
|
|
125
125
|
```ts
|
|
126
126
|
githubPlugin({
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { PluginCredentialResult, PluginGrant, PluginProviderAccount, PluginStoredTokens, IssueCredentialHookContext } from "@sentry/junior-plugin-api";
|
|
2
|
+
import { type GitHubAppPermissions } from "./permissions.js";
|
|
3
|
+
export type JsonRecord = Record<string, unknown>;
|
|
4
|
+
export type GitHubGrantName = "installation-read" | "installation-write" | "user-read" | "user-write";
|
|
5
|
+
export type GitHubGrantReason = "github.api-read" | "github.asset-upload" | "github.git-read" | "github.graphql-read" | "github.installation-write" | "github.user-read" | "github.user-write";
|
|
6
|
+
export type GitHubGrant = PluginGrant & {
|
|
7
|
+
name: GitHubGrantName;
|
|
8
|
+
reason: GitHubGrantReason;
|
|
9
|
+
};
|
|
10
|
+
interface GitHubRequestParams {
|
|
11
|
+
body?: unknown;
|
|
12
|
+
method?: string;
|
|
13
|
+
token: string;
|
|
14
|
+
}
|
|
15
|
+
interface UserCredentialOptions {
|
|
16
|
+
clientIdEnv: string;
|
|
17
|
+
clientSecretEnv: string;
|
|
18
|
+
userScope?: string;
|
|
19
|
+
}
|
|
20
|
+
interface InstallationCredentialBaseOptions {
|
|
21
|
+
appIdEnv: string;
|
|
22
|
+
installationIdEnv: string;
|
|
23
|
+
privateKeyEnv: string;
|
|
24
|
+
}
|
|
25
|
+
type InstallationCredentialOptions = InstallationCredentialBaseOptions & ({
|
|
26
|
+
loadPermissions?: never;
|
|
27
|
+
permissions?: GitHubAppPermissions;
|
|
28
|
+
repositories: string[];
|
|
29
|
+
} | {
|
|
30
|
+
loadPermissions?: never;
|
|
31
|
+
permissions: GitHubAppPermissions;
|
|
32
|
+
repositories?: never;
|
|
33
|
+
} | {
|
|
34
|
+
loadPermissions: LoadInstallationReadPermissions;
|
|
35
|
+
permissions?: never;
|
|
36
|
+
repositories?: never;
|
|
37
|
+
});
|
|
38
|
+
type LoadInstallationReadPermissions = (input: {
|
|
39
|
+
appJwt: string;
|
|
40
|
+
installationId: number;
|
|
41
|
+
}) => Promise<Record<string, "read">>;
|
|
42
|
+
interface GitHubRepository {
|
|
43
|
+
name: string;
|
|
44
|
+
owner: string;
|
|
45
|
+
}
|
|
46
|
+
export declare const GITHUB_APP_ID_ENV = "GITHUB_APP_ID";
|
|
47
|
+
export declare const GITHUB_APP_PRIVATE_KEY_ENV = "GITHUB_APP_PRIVATE_KEY";
|
|
48
|
+
export declare const GITHUB_INSTALLATION_ID_ENV = "GITHUB_INSTALLATION_ID";
|
|
49
|
+
export declare const GITHUB_AUTH_TOKEN_ENV = "GITHUB_TOKEN";
|
|
50
|
+
export declare const GITHUB_AUTH_TOKEN_PLACEHOLDER = "ghp_host_managed_credential";
|
|
51
|
+
export declare const GITHUB_GRAPHQL_RESPONSE_BODY_LIMIT_BYTES: number;
|
|
52
|
+
export declare const HTTP_READ_METHODS: Set<string>;
|
|
53
|
+
export declare const USER_TOKEN_GRANTS: Set<string>;
|
|
54
|
+
export declare const CREATE_TOOL_ROUTING_GUIDANCE = "This is a Junior tool-routing denial, not a GitHub permission failure. Do not ask the user for GitHub permissions; retry with the required Junior tool.";
|
|
55
|
+
export declare const USER_WRITE_REQUIREMENTS: string[];
|
|
56
|
+
export declare class GitHubPluginSetupError extends Error {
|
|
57
|
+
constructor(message: string);
|
|
58
|
+
}
|
|
59
|
+
/** Return whether a provider value is a JSON object. */
|
|
60
|
+
export declare function isRecord(value: unknown): value is JsonRecord;
|
|
61
|
+
/** Read a non-empty GitHub plugin environment value. */
|
|
62
|
+
export declare function readEnv(name: string): string | undefined;
|
|
63
|
+
/** Read a required GitHub plugin environment value. */
|
|
64
|
+
export declare function requireEnv(name: string): string;
|
|
65
|
+
/** Normalize configured GitHub OAuth scopes. */
|
|
66
|
+
export declare function normalizeScopeList(scopes?: string[]): string[];
|
|
67
|
+
/** Send an authenticated request to the GitHub API. */
|
|
68
|
+
export declare function githubRequest(apiBase: string, path: string, params: GitHubRequestParams): Promise<unknown>;
|
|
69
|
+
/** Return a credential result for invalid GitHub App configuration. */
|
|
70
|
+
export declare function credentialUnavailable(message: string): PluginCredentialResult;
|
|
71
|
+
/** Parse a GitHub repository from an API or Git URL. */
|
|
72
|
+
export declare function githubRepositoryFromUrl(upstreamUrl: URL): GitHubRepository | undefined;
|
|
73
|
+
/** Build the stable lease scope for a GitHub repository. */
|
|
74
|
+
export declare function githubRepositoryLeaseScope(repository: GitHubRepository): string;
|
|
75
|
+
/** Parse the repository bound to an installation-write lease. */
|
|
76
|
+
export declare function githubRepositoryFromLeaseScope(leaseScope: string | undefined): GitHubRepository;
|
|
77
|
+
/** Resolve the GitHub account associated with stored user tokens. */
|
|
78
|
+
export declare function resolveUserAccount(tokens: PluginStoredTokens): Promise<PluginProviderAccount>;
|
|
79
|
+
/** Issue a bounded GitHub user credential for an approved grant. */
|
|
80
|
+
export declare function issueUserCredential(ctx: IssueCredentialHookContext, options: UserCredentialOptions): Promise<PluginCredentialResult>;
|
|
81
|
+
/** Issue a bounded raw token for plugin-owned GitHub API calls. */
|
|
82
|
+
export declare function issueInstallationToken(options: InstallationCredentialOptions): Promise<{
|
|
83
|
+
expiresAtMs: number;
|
|
84
|
+
token: string;
|
|
85
|
+
}>;
|
|
86
|
+
/** Issue a bounded GitHub App installation credential. */
|
|
87
|
+
export declare function issueInstallationCredential(options: InstallationCredentialOptions): Promise<PluginCredentialResult>;
|
|
88
|
+
/** Cache the installation's read permissions for one lease period. */
|
|
89
|
+
export declare function createPermissionCache(): LoadInstallationReadPermissions;
|
|
90
|
+
export {};
|
package/dist/db/schema.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const githubPullRequestStateSchema: z.ZodEnum<{
|
|
3
|
-
merged: "merged";
|
|
4
3
|
open: "open";
|
|
4
|
+
merged: "merged";
|
|
5
5
|
closed_unmerged: "closed_unmerged";
|
|
6
6
|
}>;
|
|
7
7
|
export type GitHubPullRequestState = z.output<typeof githubPullRequestStateSchema>;
|
|
@@ -308,7 +308,7 @@ export declare const juniorGitHubPullRequests: import("drizzle-orm/pg-core").PgT
|
|
|
308
308
|
tableName: "junior_github_pull_requests";
|
|
309
309
|
dataType: "string";
|
|
310
310
|
columnType: "PgText";
|
|
311
|
-
data: "
|
|
311
|
+
data: "open" | "merged" | "closed_unmerged";
|
|
312
312
|
driverParam: string;
|
|
313
313
|
notNull: true;
|
|
314
314
|
hasDefault: false;
|
|
@@ -320,7 +320,7 @@ export declare const juniorGitHubPullRequests: import("drizzle-orm/pg-core").PgT
|
|
|
320
320
|
identity: undefined;
|
|
321
321
|
generated: undefined;
|
|
322
322
|
}, {}, {
|
|
323
|
-
$type: "
|
|
323
|
+
$type: "open" | "merged" | "closed_unmerged";
|
|
324
324
|
}>;
|
|
325
325
|
commitComposition: import("drizzle-orm/pg-core").PgColumn<{
|
|
326
326
|
name: "commit_composition";
|
|
@@ -857,7 +857,7 @@ export declare const githubSqlSchema: {
|
|
|
857
857
|
tableName: "junior_github_pull_requests";
|
|
858
858
|
dataType: "string";
|
|
859
859
|
columnType: "PgText";
|
|
860
|
-
data: "
|
|
860
|
+
data: "open" | "merged" | "closed_unmerged";
|
|
861
861
|
driverParam: string;
|
|
862
862
|
notNull: true;
|
|
863
863
|
hasDefault: false;
|
|
@@ -869,7 +869,7 @@ export declare const githubSqlSchema: {
|
|
|
869
869
|
identity: undefined;
|
|
870
870
|
generated: undefined;
|
|
871
871
|
}, {}, {
|
|
872
|
-
$type: "
|
|
872
|
+
$type: "open" | "merged" | "closed_unmerged";
|
|
873
873
|
}>;
|
|
874
874
|
commitComposition: import("drizzle-orm/pg-core").PgColumn<{
|
|
875
875
|
name: "commit_composition";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git identity and commit-hook setup for GitHub sandboxes.
|
|
3
|
+
*/
|
|
4
|
+
import type { Actor, SandboxPrepareHookContext } from "@sentry/junior-plugin-api";
|
|
5
|
+
/**
|
|
6
|
+
* Build `Co-Authored-By` trailers crediting human run actors.
|
|
7
|
+
*
|
|
8
|
+
* `run.actors` is attribution only (see `multi-actor-runs.md`): a steerer
|
|
9
|
+
* without a resolvable name and email is silently omitted rather than
|
|
10
|
+
* denying the commit. Dedupes by identity and resolved email so the same human
|
|
11
|
+
* under two display profiles, or an actor matching the bot identity, only ever
|
|
12
|
+
* produces one line.
|
|
13
|
+
*/
|
|
14
|
+
export declare function additionalActorCoauthorTrailers(args: {
|
|
15
|
+
actors?: Actor[];
|
|
16
|
+
botEmail: string;
|
|
17
|
+
}): string[];
|
|
18
|
+
/** Build the hook that replaces model-supplied commit attribution. */
|
|
19
|
+
export declare function prepareCommitMsgHook(): string;
|
|
20
|
+
/** Set one global Git option inside the prepared sandbox. */
|
|
21
|
+
export declare function configureGit(ctx: SandboxPrepareHookContext, key: string, value: string): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,40 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { type GitHubAppPermissions } from "./permissions.js";
|
|
1
|
+
export { githubPlugin, type GitHubPluginOptions } from "./plugin.js";
|
|
3
2
|
export type { GitHubAppPermissionLevel } from "./permissions.js";
|
|
4
|
-
/** Configure the built-in GitHub plugin manifest and hooks. */
|
|
5
|
-
export interface GitHubPluginOptions {
|
|
6
|
-
/**
|
|
7
|
-
* Extra OAuth `scope` values to request during GitHub App user authorization.
|
|
8
|
-
*
|
|
9
|
-
* GitHub App user tokens report empty scopes, so Junior treats this as a
|
|
10
|
-
* local reauthorization contract only. Effective access still comes from the
|
|
11
|
-
* app permissions, installation repositories, and requesting user's access.
|
|
12
|
-
*/
|
|
13
|
-
additionalUserScopes?: string[];
|
|
14
|
-
/**
|
|
15
|
-
* GitHub App permissions Junior should expose as capabilities and downscope
|
|
16
|
-
* to read for installation-read tokens.
|
|
17
|
-
*
|
|
18
|
-
* Keys may use GitHub permission names with underscores or hyphens. Junior
|
|
19
|
-
* records these as plugin capabilities. Installation-write tokens inherit
|
|
20
|
-
* the App installation's complete permission envelope.
|
|
21
|
-
* GitHub remains the source of truth for whether a permission exists.
|
|
22
|
-
*/
|
|
23
|
-
appPermissions?: GitHubAppPermissions;
|
|
24
|
-
/** Environment variable containing the GitHub App id. */
|
|
25
|
-
appIdEnv?: string;
|
|
26
|
-
/** Environment variable containing Junior's Git committer email. */
|
|
27
|
-
botEmailEnv?: string;
|
|
28
|
-
/** Environment variable containing Junior's Git committer name. */
|
|
29
|
-
botNameEnv?: string;
|
|
30
|
-
/** Environment variable containing the GitHub App OAuth client id. */
|
|
31
|
-
clientIdEnv?: string;
|
|
32
|
-
/** Environment variable containing the GitHub App OAuth client secret. */
|
|
33
|
-
clientSecretEnv?: string;
|
|
34
|
-
/** Environment variable containing the GitHub App installation id. */
|
|
35
|
-
installationIdEnv?: string;
|
|
36
|
-
/** Environment variable containing the GitHub App private key. */
|
|
37
|
-
privateKeyEnv?: string;
|
|
38
|
-
}
|
|
39
|
-
/** Register GitHub runtime hooks for repository workflows. */
|
|
40
|
-
export declare function githubPlugin(options?: GitHubPluginOptions): PluginRegistration;
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,7 @@ import {
|
|
|
3
3
|
normalizeGitHubResourceEvents
|
|
4
4
|
} from "./chunk-JDNXIBCQ.js";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
7
|
-
import { createPrivateKey, createSign } from "crypto";
|
|
6
|
+
// src/plugin.ts
|
|
8
7
|
import {
|
|
9
8
|
defineJuniorPlugin,
|
|
10
9
|
EgressPolicyDenied
|
|
@@ -65,15 +64,6 @@ function readGrantPermissions(permissions) {
|
|
|
65
64
|
}
|
|
66
65
|
return readOnly;
|
|
67
66
|
}
|
|
68
|
-
function permissionCapabilities(permissions) {
|
|
69
|
-
if (permissions === void 0) {
|
|
70
|
-
return void 0;
|
|
71
|
-
}
|
|
72
|
-
return Object.entries(permissions).map(([normalizedScope, rawLevel]) => {
|
|
73
|
-
const scope = normalizedScope.replace(/_/g, "-");
|
|
74
|
-
return `github.${scope}.${rawLevel}`;
|
|
75
|
-
}).sort();
|
|
76
|
-
}
|
|
77
67
|
|
|
78
68
|
// src/tools/create-issue.ts
|
|
79
69
|
import {
|
|
@@ -528,6 +518,7 @@ import { z as z2 } from "zod";
|
|
|
528
518
|
import { subscribableResourceSchema } from "@sentry/junior-plugin-api";
|
|
529
519
|
var GITHUB_PULL_REQUEST_CREATE_IDEMPOTENCY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
530
520
|
var GITHUB_PULL_REQUEST_CREATE_LOCK_TTL_MS = 6e4;
|
|
521
|
+
var RESOURCE_LINK_LABEL_MAX_LENGTH = 256;
|
|
531
522
|
var GitHubPullRequestCreateRejectedError = class extends Error {
|
|
532
523
|
status;
|
|
533
524
|
constructor(message, status) {
|
|
@@ -760,6 +751,20 @@ function gitHubPullRequestToolResult(input, result) {
|
|
|
760
751
|
});
|
|
761
752
|
return { ...result, ...subscribable ? { subscribable } : {} };
|
|
762
753
|
}
|
|
754
|
+
async function annotatePullRequest(ctx, input, result) {
|
|
755
|
+
const repo = parseRepo2(input.repo);
|
|
756
|
+
const label = `${repo.owner}/${repo.name} #${result.number}: ${nonEmptyString3(input.title, "title")}`.slice(
|
|
757
|
+
0,
|
|
758
|
+
RESOURCE_LINK_LABEL_MAX_LENGTH
|
|
759
|
+
);
|
|
760
|
+
await ctx.annotations?.upsert({
|
|
761
|
+
kind: "resource_link",
|
|
762
|
+
key: `${repo.owner.toLowerCase()}/${repo.name.toLowerCase()}#${result.number}`,
|
|
763
|
+
label,
|
|
764
|
+
url: result.url,
|
|
765
|
+
status: input.draft ? "draft" : "open"
|
|
766
|
+
});
|
|
767
|
+
}
|
|
763
768
|
function gitHubPullRequestStructuredResult(input, result) {
|
|
764
769
|
const data = gitHubPullRequestToolResult(input, result);
|
|
765
770
|
return {
|
|
@@ -789,12 +794,15 @@ function createGitHubPullRequestTool(ctx) {
|
|
|
789
794
|
async () => {
|
|
790
795
|
const state = createPullRequestState(await ctx.state.get(key));
|
|
791
796
|
if (state?.status === "completed") {
|
|
797
|
+
const completedInput = state.input ?? parsedInput;
|
|
798
|
+
const completedResult = {
|
|
799
|
+
number: state.number,
|
|
800
|
+
url: state.url
|
|
801
|
+
};
|
|
802
|
+
await annotatePullRequest(ctx, completedInput, completedResult);
|
|
792
803
|
return gitHubPullRequestStructuredResult(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
number: state.number,
|
|
796
|
-
url: state.url
|
|
797
|
-
}
|
|
804
|
+
completedInput,
|
|
805
|
+
completedResult
|
|
798
806
|
);
|
|
799
807
|
}
|
|
800
808
|
if (state?.status === "pending") {
|
|
@@ -833,6 +841,7 @@ function createGitHubPullRequestTool(ctx) {
|
|
|
833
841
|
{ cause: error }
|
|
834
842
|
);
|
|
835
843
|
}
|
|
844
|
+
await annotatePullRequest(ctx, parsedInput, result);
|
|
836
845
|
return gitHubPullRequestStructuredResult(parsedInput, result);
|
|
837
846
|
} catch (error) {
|
|
838
847
|
if (isEgressAuthRequired2(error) || isDefinitiveGitHubPullRequestCreateRejection(error)) {
|
|
@@ -1828,10 +1837,14 @@ var costWindowSchema = z10.object({
|
|
|
1828
1837
|
}));
|
|
1829
1838
|
var repositoryCostSchema = z10.object({
|
|
1830
1839
|
issueCostUsd: z10.number().nonnegative().nullable(),
|
|
1840
|
+
medianIssueCostUsd: z10.number().nonnegative().nullable(),
|
|
1841
|
+
medianPullRequestCostUsd: z10.number().nonnegative().nullable(),
|
|
1831
1842
|
pullRequestCostUsd: z10.number().nonnegative().nullable(),
|
|
1832
1843
|
repository: z10.string().min(1)
|
|
1833
1844
|
}).strict().transform((row) => ({
|
|
1834
1845
|
issueCostUsd: row.issueCostUsd ?? void 0,
|
|
1846
|
+
medianIssueCostUsd: row.medianIssueCostUsd ?? void 0,
|
|
1847
|
+
medianPullRequestCostUsd: row.medianPullRequestCostUsd ?? void 0,
|
|
1835
1848
|
pullRequestCostUsd: row.pullRequestCostUsd ?? void 0,
|
|
1836
1849
|
repository: row.repository
|
|
1837
1850
|
}));
|
|
@@ -2048,7 +2061,8 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2048
2061
|
WITH pull_request_entities AS (
|
|
2049
2062
|
SELECT
|
|
2050
2063
|
${pullRequests.repositoryFullName} AS repository,
|
|
2051
|
-
conversation_ids.ids AS ids
|
|
2064
|
+
conversation_ids.ids AS ids,
|
|
2065
|
+
${conversationTreeCost} AS cost_usd
|
|
2052
2066
|
FROM ${pullRequests}
|
|
2053
2067
|
CROSS JOIN LATERAL (
|
|
2054
2068
|
SELECT ${pullRequestConversationIds} AS ids
|
|
@@ -2057,7 +2071,8 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2057
2071
|
), issue_entities AS (
|
|
2058
2072
|
SELECT
|
|
2059
2073
|
${issues.repositoryFullName} AS repository,
|
|
2060
|
-
conversation_ids.ids AS ids
|
|
2074
|
+
conversation_ids.ids AS ids,
|
|
2075
|
+
${conversationTreeCost} AS cost_usd
|
|
2061
2076
|
FROM ${issues}
|
|
2062
2077
|
CROSS JOIN LATERAL (
|
|
2063
2078
|
SELECT ${issueConversationIds} AS ids
|
|
@@ -2079,7 +2094,15 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2079
2094
|
WHERE pull_request_entities.repository = repositories.repository
|
|
2080
2095
|
) AS ids
|
|
2081
2096
|
) AS conversation_ids
|
|
2082
|
-
), 0)::double precision AS pull_request_cost_usd
|
|
2097
|
+
), 0)::double precision AS pull_request_cost_usd,
|
|
2098
|
+
(
|
|
2099
|
+
SELECT percentile_cont(0.5) WITHIN GROUP (
|
|
2100
|
+
ORDER BY pull_request_entities.cost_usd
|
|
2101
|
+
)
|
|
2102
|
+
FROM pull_request_entities
|
|
2103
|
+
WHERE pull_request_entities.repository = repositories.repository
|
|
2104
|
+
AND pull_request_entities.cost_usd > 0
|
|
2105
|
+
)::double precision AS median_pull_request_cost_usd
|
|
2083
2106
|
FROM repositories
|
|
2084
2107
|
), issue_totals AS (
|
|
2085
2108
|
SELECT
|
|
@@ -2093,21 +2116,32 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2093
2116
|
WHERE issue_entities.repository = repositories.repository
|
|
2094
2117
|
) AS ids
|
|
2095
2118
|
) AS conversation_ids
|
|
2096
|
-
), 0)::double precision AS issue_cost_usd
|
|
2119
|
+
), 0)::double precision AS issue_cost_usd,
|
|
2120
|
+
(
|
|
2121
|
+
SELECT percentile_cont(0.5) WITHIN GROUP (
|
|
2122
|
+
ORDER BY issue_entities.cost_usd
|
|
2123
|
+
)
|
|
2124
|
+
FROM issue_entities
|
|
2125
|
+
WHERE issue_entities.repository = repositories.repository
|
|
2126
|
+
AND issue_entities.cost_usd > 0
|
|
2127
|
+
)::double precision AS median_issue_cost_usd
|
|
2097
2128
|
FROM repositories
|
|
2098
2129
|
)
|
|
2099
2130
|
SELECT
|
|
2100
2131
|
repositories.repository AS "repository",
|
|
2101
2132
|
coalesce(pull_request_totals.pull_request_cost_usd, 0)::double precision
|
|
2102
2133
|
AS "pullRequestCostUsd",
|
|
2134
|
+
pull_request_totals.median_pull_request_cost_usd
|
|
2135
|
+
AS "medianPullRequestCostUsd",
|
|
2103
2136
|
coalesce(issue_totals.issue_cost_usd, 0)::double precision
|
|
2104
|
-
AS "issueCostUsd"
|
|
2137
|
+
AS "issueCostUsd",
|
|
2138
|
+
issue_totals.median_issue_cost_usd AS "medianIssueCostUsd"
|
|
2105
2139
|
FROM repositories
|
|
2106
2140
|
LEFT JOIN pull_request_totals
|
|
2107
2141
|
ON pull_request_totals.repository = repositories.repository
|
|
2108
2142
|
LEFT JOIN issue_totals
|
|
2109
2143
|
ON issue_totals.repository = repositories.repository
|
|
2110
|
-
ORDER BY "
|
|
2144
|
+
ORDER BY "repository" ASC
|
|
2111
2145
|
`);
|
|
2112
2146
|
return z10.array(repositoryCostSchema).parse(queryRows(result));
|
|
2113
2147
|
}
|
|
@@ -2548,7 +2582,7 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2548
2582
|
{ key: "merged", label: "Merged" },
|
|
2549
2583
|
{ key: "closed", label: "Closed unmerged" },
|
|
2550
2584
|
{ key: "mergeRate", label: "Closure merge rate" },
|
|
2551
|
-
{ key: "
|
|
2585
|
+
{ key: "medianCost", label: "Median cost" }
|
|
2552
2586
|
],
|
|
2553
2587
|
records: repositories.map(({ repository, ...stats }) => ({
|
|
2554
2588
|
id: repository,
|
|
@@ -2559,8 +2593,8 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2559
2593
|
closed: String(stats.closed),
|
|
2560
2594
|
juniorOnly: String(stats.juniorOnly),
|
|
2561
2595
|
mergeRate: formatPercent(stats.mergeRate),
|
|
2562
|
-
|
|
2563
|
-
repositoryCostByName.get(repository)?.
|
|
2596
|
+
medianCost: formatCostUsd(
|
|
2597
|
+
repositoryCostByName.get(repository)?.medianPullRequestCostUsd
|
|
2564
2598
|
)
|
|
2565
2599
|
}
|
|
2566
2600
|
}))
|
|
@@ -2575,7 +2609,7 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2575
2609
|
{ key: "duplicate", label: "Duplicate" },
|
|
2576
2610
|
{ key: "notPlanned", label: "Not planned" },
|
|
2577
2611
|
{ key: "unknown", label: "Unknown reason" },
|
|
2578
|
-
{ key: "
|
|
2612
|
+
{ key: "medianCost", label: "Median cost" }
|
|
2579
2613
|
],
|
|
2580
2614
|
records: issueRepositories.map(({ repository, ...stats }) => ({
|
|
2581
2615
|
id: repository,
|
|
@@ -2586,8 +2620,8 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2586
2620
|
duplicate: String(stats.closedDuplicate),
|
|
2587
2621
|
notPlanned: String(stats.closedNotPlanned),
|
|
2588
2622
|
unknown: String(stats.closedUnknown),
|
|
2589
|
-
|
|
2590
|
-
repositoryCostByName.get(repository)?.
|
|
2623
|
+
medianCost: formatCostUsd(
|
|
2624
|
+
repositoryCostByName.get(repository)?.medianIssueCostUsd
|
|
2591
2625
|
)
|
|
2592
2626
|
}
|
|
2593
2627
|
}))
|
|
@@ -2649,87 +2683,7 @@ async function classifyGitHubPullRequestCommitComposition(args) {
|
|
|
2649
2683
|
return foundCommit ? "junior_only" : void 0;
|
|
2650
2684
|
}
|
|
2651
2685
|
|
|
2652
|
-
// src/
|
|
2653
|
-
var GITHUB_APP_ID_ENV = "GITHUB_APP_ID";
|
|
2654
|
-
var GITHUB_APP_PRIVATE_KEY_ENV = "GITHUB_APP_PRIVATE_KEY";
|
|
2655
|
-
var GITHUB_INSTALLATION_ID_ENV = "GITHUB_INSTALLATION_ID";
|
|
2656
|
-
var GITHUB_AUTH_TOKEN_ENV = "GITHUB_TOKEN";
|
|
2657
|
-
var GITHUB_AUTH_TOKEN_PLACEHOLDER = "ghp_host_managed_credential";
|
|
2658
|
-
var MAX_LEASE_MS = 60 * 60 * 1e3;
|
|
2659
|
-
var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
2660
|
-
var USER_REFRESH_TIMEOUT_MS = 2e4;
|
|
2661
|
-
var GITHUB_GRAPHQL_RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
2662
|
-
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
2663
|
-
var USER_TOKEN_GRANTS = /* @__PURE__ */ new Set(["user-read", "user-write"]);
|
|
2664
|
-
var CREATE_TOOL_ROUTING_GUIDANCE = "This is a Junior tool-routing denial, not a GitHub permission failure. Do not ask the user for GitHub permissions; retry with the required Junior tool.";
|
|
2665
|
-
var USER_WRITE_REQUIREMENTS = [
|
|
2666
|
-
"requesting GitHub user permission to perform this operation"
|
|
2667
|
-
];
|
|
2668
|
-
var GITHUB_CREDENTIAL_DOMAINS = ["api.github.com", "github.com"];
|
|
2669
|
-
var GITHUB_ASSET_UPLOAD_CREDENTIAL_DOMAINS = [
|
|
2670
|
-
...GITHUB_CREDENTIAL_DOMAINS,
|
|
2671
|
-
"uploads.github.com"
|
|
2672
|
-
];
|
|
2673
|
-
var GitHubUserRefreshRejectedError = class extends Error {
|
|
2674
|
-
constructor(message) {
|
|
2675
|
-
super(message);
|
|
2676
|
-
this.name = "GitHubUserRefreshRejectedError";
|
|
2677
|
-
}
|
|
2678
|
-
};
|
|
2679
|
-
var GitHubRequestError = class extends Error {
|
|
2680
|
-
status;
|
|
2681
|
-
constructor(message, status) {
|
|
2682
|
-
super(message);
|
|
2683
|
-
this.name = "GitHubRequestError";
|
|
2684
|
-
this.status = status;
|
|
2685
|
-
}
|
|
2686
|
-
};
|
|
2687
|
-
var GitHubPluginSetupError = class extends Error {
|
|
2688
|
-
constructor(message) {
|
|
2689
|
-
super(message);
|
|
2690
|
-
this.name = "GitHubPluginSetupError";
|
|
2691
|
-
}
|
|
2692
|
-
};
|
|
2693
|
-
function isRecord(value) {
|
|
2694
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2695
|
-
}
|
|
2696
|
-
function readEnv(name) {
|
|
2697
|
-
const value = process.env[name];
|
|
2698
|
-
if (typeof value !== "string") {
|
|
2699
|
-
return void 0;
|
|
2700
|
-
}
|
|
2701
|
-
const trimmed = value.trim();
|
|
2702
|
-
return trimmed ? trimmed : void 0;
|
|
2703
|
-
}
|
|
2704
|
-
function requireEnv(name) {
|
|
2705
|
-
const value = readEnv(name);
|
|
2706
|
-
if (!value) {
|
|
2707
|
-
throw new GitHubPluginSetupError(`Missing ${name}`);
|
|
2708
|
-
}
|
|
2709
|
-
return value;
|
|
2710
|
-
}
|
|
2711
|
-
function normalizeScopeList(scopes) {
|
|
2712
|
-
return [
|
|
2713
|
-
...new Set(
|
|
2714
|
-
(scopes ?? []).flatMap((scope) => String(scope).split(/\s+/)).map((scope) => scope.trim()).filter(Boolean)
|
|
2715
|
-
)
|
|
2716
|
-
].sort();
|
|
2717
|
-
}
|
|
2718
|
-
function normalizeOAuthScope(scope) {
|
|
2719
|
-
const normalized = normalizeScopeList(scope ? [scope] : []);
|
|
2720
|
-
return normalized.length ? normalized.join(" ") : void 0;
|
|
2721
|
-
}
|
|
2722
|
-
function hasRequiredOAuthScope(storedScope, requiredScope) {
|
|
2723
|
-
const required = normalizeScopeList(requiredScope ? [requiredScope] : []);
|
|
2724
|
-
if (required.length === 0) {
|
|
2725
|
-
return true;
|
|
2726
|
-
}
|
|
2727
|
-
const stored = new Set(normalizeScopeList(storedScope ? [storedScope] : []));
|
|
2728
|
-
if (stored.size === 0) {
|
|
2729
|
-
return false;
|
|
2730
|
-
}
|
|
2731
|
-
return required.every((scope) => stored.has(scope));
|
|
2732
|
-
}
|
|
2686
|
+
// src/git-config.ts
|
|
2733
2687
|
function cleanIdentityPart(value) {
|
|
2734
2688
|
return String(value ?? "").replaceAll("\n", " ").replaceAll("\r", " ").replace(/[<>]/g, "").trim();
|
|
2735
2689
|
}
|
|
@@ -2937,6 +2891,92 @@ async function configureGit(ctx, key, value) {
|
|
|
2937
2891
|
);
|
|
2938
2892
|
}
|
|
2939
2893
|
}
|
|
2894
|
+
|
|
2895
|
+
// src/credential-support.ts
|
|
2896
|
+
import { createPrivateKey, createSign } from "crypto";
|
|
2897
|
+
var GITHUB_APP_ID_ENV = "GITHUB_APP_ID";
|
|
2898
|
+
var GITHUB_APP_PRIVATE_KEY_ENV = "GITHUB_APP_PRIVATE_KEY";
|
|
2899
|
+
var GITHUB_INSTALLATION_ID_ENV = "GITHUB_INSTALLATION_ID";
|
|
2900
|
+
var GITHUB_AUTH_TOKEN_ENV = "GITHUB_TOKEN";
|
|
2901
|
+
var GITHUB_AUTH_TOKEN_PLACEHOLDER = "ghp_host_managed_credential";
|
|
2902
|
+
var MAX_LEASE_MS = 60 * 60 * 1e3;
|
|
2903
|
+
var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
2904
|
+
var USER_REFRESH_TIMEOUT_MS = 2e4;
|
|
2905
|
+
var GITHUB_GRAPHQL_RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
2906
|
+
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
2907
|
+
var USER_TOKEN_GRANTS = /* @__PURE__ */ new Set(["user-read", "user-write"]);
|
|
2908
|
+
var CREATE_TOOL_ROUTING_GUIDANCE = "This is a Junior tool-routing denial, not a GitHub permission failure. Do not ask the user for GitHub permissions; retry with the required Junior tool.";
|
|
2909
|
+
var USER_WRITE_REQUIREMENTS = [
|
|
2910
|
+
"requesting GitHub user permission to perform this operation"
|
|
2911
|
+
];
|
|
2912
|
+
var GITHUB_CREDENTIAL_DOMAINS = ["api.github.com", "github.com"];
|
|
2913
|
+
var GITHUB_ASSET_UPLOAD_CREDENTIAL_DOMAINS = [
|
|
2914
|
+
...GITHUB_CREDENTIAL_DOMAINS,
|
|
2915
|
+
"uploads.github.com"
|
|
2916
|
+
];
|
|
2917
|
+
var GitHubUserRefreshRejectedError = class extends Error {
|
|
2918
|
+
constructor(message) {
|
|
2919
|
+
super(message);
|
|
2920
|
+
this.name = "GitHubUserRefreshRejectedError";
|
|
2921
|
+
}
|
|
2922
|
+
};
|
|
2923
|
+
var GitHubRequestError = class extends Error {
|
|
2924
|
+
status;
|
|
2925
|
+
constructor(message, status) {
|
|
2926
|
+
super(message);
|
|
2927
|
+
this.name = "GitHubRequestError";
|
|
2928
|
+
this.status = status;
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
var GitHubPluginSetupError = class extends Error {
|
|
2932
|
+
constructor(message) {
|
|
2933
|
+
super(message);
|
|
2934
|
+
this.name = "GitHubPluginSetupError";
|
|
2935
|
+
}
|
|
2936
|
+
};
|
|
2937
|
+
function isRecord(value) {
|
|
2938
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2939
|
+
}
|
|
2940
|
+
function readEnv(name) {
|
|
2941
|
+
const value = process.env[name];
|
|
2942
|
+
if (typeof value !== "string") {
|
|
2943
|
+
return void 0;
|
|
2944
|
+
}
|
|
2945
|
+
const trimmed = value.trim();
|
|
2946
|
+
return trimmed ? trimmed : void 0;
|
|
2947
|
+
}
|
|
2948
|
+
function requireEnv(name) {
|
|
2949
|
+
const value = readEnv(name);
|
|
2950
|
+
if (!value) {
|
|
2951
|
+
throw new GitHubPluginSetupError(`Missing ${name}`);
|
|
2952
|
+
}
|
|
2953
|
+
return value;
|
|
2954
|
+
}
|
|
2955
|
+
function normalizeScopeList(scopes) {
|
|
2956
|
+
return [
|
|
2957
|
+
...new Set(
|
|
2958
|
+
(scopes ?? []).flatMap((scope) => String(scope).split(/\s+/)).map((scope) => scope.trim()).filter(Boolean)
|
|
2959
|
+
)
|
|
2960
|
+
].sort();
|
|
2961
|
+
}
|
|
2962
|
+
function normalizeOAuthScope(scope) {
|
|
2963
|
+
const normalized = normalizeScopeList(scope ? [scope] : []);
|
|
2964
|
+
return normalized.length ? normalized.join(" ") : void 0;
|
|
2965
|
+
}
|
|
2966
|
+
function hasRequiredOAuthScope(storedScope, requiredScope) {
|
|
2967
|
+
const required = normalizeScopeList(requiredScope ? [requiredScope] : []);
|
|
2968
|
+
if (required.length === 0) {
|
|
2969
|
+
return true;
|
|
2970
|
+
}
|
|
2971
|
+
const stored = new Set(normalizeScopeList(storedScope ? [storedScope] : []));
|
|
2972
|
+
if (stored.size === 0) {
|
|
2973
|
+
return false;
|
|
2974
|
+
}
|
|
2975
|
+
return required.every((scope) => stored.has(scope));
|
|
2976
|
+
}
|
|
2977
|
+
function isGitHubApiUrl(upstreamUrl) {
|
|
2978
|
+
return upstreamUrl.hostname.toLowerCase() === "api.github.com";
|
|
2979
|
+
}
|
|
2940
2980
|
function base64Url(input) {
|
|
2941
2981
|
return Buffer.from(input).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
2942
2982
|
}
|
|
@@ -3437,6 +3477,8 @@ function createPermissionCache() {
|
|
|
3437
3477
|
return await pending;
|
|
3438
3478
|
};
|
|
3439
3479
|
}
|
|
3480
|
+
|
|
3481
|
+
// src/plugin.ts
|
|
3440
3482
|
function githubSmartHttpAccess(upstreamUrl) {
|
|
3441
3483
|
const pathname = upstreamUrl.pathname.toLowerCase();
|
|
3442
3484
|
const service = upstreamUrl.searchParams.get("service")?.toLowerCase();
|
|
@@ -3455,14 +3497,14 @@ function githubSmartHttpAccess(upstreamUrl) {
|
|
|
3455
3497
|
function isGitHubGraphqlUrl(upstreamUrl) {
|
|
3456
3498
|
return upstreamUrl.hostname.toLowerCase() === "api.github.com" && upstreamUrl.pathname.toLowerCase().endsWith("/graphql");
|
|
3457
3499
|
}
|
|
3458
|
-
function
|
|
3500
|
+
function isGitHubApiUrl2(upstreamUrl) {
|
|
3459
3501
|
return upstreamUrl.hostname.toLowerCase() === "api.github.com";
|
|
3460
3502
|
}
|
|
3461
3503
|
function isGitHubAssetUploadRequest(method, upstreamUrl) {
|
|
3462
3504
|
return method === "POST" && upstreamUrl.hostname.toLowerCase() === "uploads.github.com" && upstreamUrl.pathname === "/user-attachments/assets";
|
|
3463
3505
|
}
|
|
3464
3506
|
function githubUserReadReason(method, upstreamUrl) {
|
|
3465
|
-
if (method !== "GET" || !
|
|
3507
|
+
if (method !== "GET" || !isGitHubApiUrl2(upstreamUrl)) {
|
|
3466
3508
|
return void 0;
|
|
3467
3509
|
}
|
|
3468
3510
|
return upstreamUrl.pathname.toLowerCase() === "/user" ? "github.user-read" : void 0;
|
|
@@ -3590,7 +3632,7 @@ function shouldInspectGitHubGraphqlResponse(ctx) {
|
|
|
3590
3632
|
}
|
|
3591
3633
|
function githubApiWriteGrantName(method, upstreamUrl) {
|
|
3592
3634
|
const pathname = upstreamUrl.pathname.toLowerCase();
|
|
3593
|
-
if (!
|
|
3635
|
+
if (!isGitHubApiUrl2(upstreamUrl)) {
|
|
3594
3636
|
return void 0;
|
|
3595
3637
|
}
|
|
3596
3638
|
if (method === "POST" && /^\/repos\/[^/]+\/[^/]+\/actions\/workflows\/[^/]+\/dispatches$/.test(
|
|
@@ -3632,10 +3674,10 @@ function githubApiWriteGrantName(method, upstreamUrl) {
|
|
|
3632
3674
|
return void 0;
|
|
3633
3675
|
}
|
|
3634
3676
|
function isGitHubIssueCreateRestRequest(method, upstreamUrl) {
|
|
3635
|
-
return method === "POST" &&
|
|
3677
|
+
return method === "POST" && isGitHubApiUrl2(upstreamUrl) && /^\/repos\/[^/]+\/[^/]+\/issues$/.test(upstreamUrl.pathname.toLowerCase());
|
|
3636
3678
|
}
|
|
3637
3679
|
function isGitHubPullCreateRestRequest(method, upstreamUrl) {
|
|
3638
|
-
return method === "POST" &&
|
|
3680
|
+
return method === "POST" && isGitHubApiUrl2(upstreamUrl) && /^\/repos\/[^/]+\/[^/]+\/pulls$/.test(upstreamUrl.pathname.toLowerCase());
|
|
3639
3681
|
}
|
|
3640
3682
|
function isGitHubIssueCreateGraphqlMutation(method, upstreamUrl, bodyText) {
|
|
3641
3683
|
if (method !== "POST" || !isGitHubGraphqlUrl(upstreamUrl)) {
|
|
@@ -3794,7 +3836,6 @@ function githubPlugin(options = {}) {
|
|
|
3794
3836
|
const declaredAppPermissions = normalizePermissions(options.appPermissions);
|
|
3795
3837
|
const declaredReadPermissions = declaredAppPermissions ? readGrantPermissions(declaredAppPermissions) : void 0;
|
|
3796
3838
|
const loadReadPermissions = createPermissionCache();
|
|
3797
|
-
const appCapabilities = permissionCapabilities(declaredAppPermissions);
|
|
3798
3839
|
const userScopes = normalizeScopeList(options.additionalUserScopes);
|
|
3799
3840
|
const userScope = userScopes.length ? userScopes.join(" ") : void 0;
|
|
3800
3841
|
return defineJuniorPlugin({
|
|
@@ -3803,7 +3844,6 @@ function githubPlugin(options = {}) {
|
|
|
3803
3844
|
name: "github",
|
|
3804
3845
|
displayName: "GitHub",
|
|
3805
3846
|
description: "GitHub issue, pull request, and repository workflows via GitHub App",
|
|
3806
|
-
...appCapabilities ? { capabilities: appCapabilities } : {},
|
|
3807
3847
|
configKeys: ["org", "repo"],
|
|
3808
3848
|
domains: ["api.github.com", "github.com", "uploads.github.com"],
|
|
3809
3849
|
envVars: {
|
package/dist/outcomes/cost.d.ts
CHANGED
|
@@ -21,14 +21,20 @@ declare const costWindowSchema: z.ZodPipe<z.ZodObject<{
|
|
|
21
21
|
}>>;
|
|
22
22
|
declare const repositoryCostSchema: z.ZodPipe<z.ZodObject<{
|
|
23
23
|
issueCostUsd: z.ZodNullable<z.ZodNumber>;
|
|
24
|
+
medianIssueCostUsd: z.ZodNullable<z.ZodNumber>;
|
|
25
|
+
medianPullRequestCostUsd: z.ZodNullable<z.ZodNumber>;
|
|
24
26
|
pullRequestCostUsd: z.ZodNullable<z.ZodNumber>;
|
|
25
27
|
repository: z.ZodString;
|
|
26
28
|
}, z.core.$strict>, z.ZodTransform<{
|
|
27
29
|
issueCostUsd: number | undefined;
|
|
30
|
+
medianIssueCostUsd: number | undefined;
|
|
31
|
+
medianPullRequestCostUsd: number | undefined;
|
|
28
32
|
pullRequestCostUsd: number | undefined;
|
|
29
33
|
repository: string;
|
|
30
34
|
}, {
|
|
31
35
|
issueCostUsd: number | null;
|
|
36
|
+
medianIssueCostUsd: number | null;
|
|
37
|
+
medianPullRequestCostUsd: number | null;
|
|
32
38
|
pullRequestCostUsd: number | null;
|
|
33
39
|
repository: string;
|
|
34
40
|
}>>;
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export type GitHubAppPermissionLevel = "admin" | "read" | "write";
|
|
2
2
|
export type GitHubAppPermissions = Record<string, GitHubAppPermissionLevel>;
|
|
3
|
-
/** Validate the configured GitHub App
|
|
3
|
+
/** Validate the configured GitHub App read-token permission declaration. */
|
|
4
4
|
export declare function normalizePermissions(permissions: GitHubAppPermissions | undefined): GitHubAppPermissions | undefined;
|
|
5
5
|
/** Build the read-only installation-token permission body. */
|
|
6
6
|
export declare function readGrantPermissions(permissions: Record<string, unknown> | undefined): Record<string, "read">;
|
|
7
|
-
/** Expose configured permissions as plugin capabilities for host policy checks. */
|
|
8
|
-
export declare function permissionCapabilities(permissions: GitHubAppPermissions | undefined): string[] | undefined;
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub plugin runtime boundary.
|
|
3
|
+
*
|
|
4
|
+
* This module composes GitHub hooks and owns GitHub egress policy.
|
|
5
|
+
*/
|
|
6
|
+
import { type PluginRegistration } from "@sentry/junior-plugin-api";
|
|
7
|
+
import { type GitHubAppPermissions } from "./permissions.js";
|
|
8
|
+
/** Configure the built-in GitHub plugin manifest and hooks. */
|
|
9
|
+
export interface GitHubPluginOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Extra OAuth `scope` values to request during GitHub App user authorization.
|
|
12
|
+
*
|
|
13
|
+
* GitHub App user tokens report empty scopes, so Junior treats this as a
|
|
14
|
+
* local reauthorization contract only. Effective access still comes from the
|
|
15
|
+
* app permissions, installation repositories, and requesting user's access.
|
|
16
|
+
*/
|
|
17
|
+
additionalUserScopes?: string[];
|
|
18
|
+
/**
|
|
19
|
+
* GitHub App permissions Junior should downscope to read for
|
|
20
|
+
* installation-read tokens.
|
|
21
|
+
*
|
|
22
|
+
* Keys may use GitHub permission names with underscores or hyphens.
|
|
23
|
+
* Installation-write tokens inherit the App installation's complete
|
|
24
|
+
* permission envelope. GitHub remains the source of truth for whether a
|
|
25
|
+
* permission exists.
|
|
26
|
+
*/
|
|
27
|
+
appPermissions?: GitHubAppPermissions;
|
|
28
|
+
/** Environment variable containing the GitHub App id. */
|
|
29
|
+
appIdEnv?: string;
|
|
30
|
+
/** Environment variable containing Junior's Git committer email. */
|
|
31
|
+
botEmailEnv?: string;
|
|
32
|
+
/** Environment variable containing Junior's Git committer name. */
|
|
33
|
+
botNameEnv?: string;
|
|
34
|
+
/** Environment variable containing the GitHub App OAuth client id. */
|
|
35
|
+
clientIdEnv?: string;
|
|
36
|
+
/** Environment variable containing the GitHub App OAuth client secret. */
|
|
37
|
+
clientSecretEnv?: string;
|
|
38
|
+
/** Environment variable containing the GitHub App installation id. */
|
|
39
|
+
installationIdEnv?: string;
|
|
40
|
+
/** Environment variable containing the GitHub App private key. */
|
|
41
|
+
privateKeyEnv?: string;
|
|
42
|
+
}
|
|
43
|
+
/** Register GitHub runtime hooks for repository workflows. */
|
|
44
|
+
export declare function githubPlugin(options?: GitHubPluginOptions): PluginRegistration;
|
|
@@ -15,8 +15,8 @@ declare const githubPullRequestOutcomeInputSchema: z.ZodObject<{
|
|
|
15
15
|
repositoryFullName: z.ZodString;
|
|
16
16
|
repositoryId: z.ZodString;
|
|
17
17
|
state: z.ZodEnum<{
|
|
18
|
-
merged: "merged";
|
|
19
18
|
open: "open";
|
|
19
|
+
merged: "merged";
|
|
20
20
|
closed_unmerged: "closed_unmerged";
|
|
21
21
|
}>;
|
|
22
22
|
updatedAt: z.ZodDate;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.116.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@sinclair/typebox": "^0.34.49",
|
|
32
32
|
"drizzle-orm": "^0.45.2",
|
|
33
33
|
"zod": "^4.4.3",
|
|
34
|
-
"@sentry/junior-plugin-api": "0.
|
|
34
|
+
"@sentry/junior-plugin-api": "0.116.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.9.1",
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
- `intercom/2x-skills@59213af`, `plugins/pr-tools/skills/attach-github-assets/SKILL.md` — upstream runtime intent and supported use cases; primary source; high confidence; MIT.
|
|
6
6
|
- `intercom/2x-skills@59213af`, `plugins/pr-tools/skills/attach-github-assets/scripts/upload.sh` — upstream endpoint, MIME mapping, and response contract; primary implementation source; high confidence; MIT.
|
|
7
7
|
- `intercom/2x-skills@59213af`, `plugins/pr-tools/LICENSE` — upstream copyright and license notice; authoritative legal source; high confidence.
|
|
8
|
-
- `packages/junior-github/src/
|
|
8
|
+
- `packages/junior-github/src/credential-support.ts` — local GitHub credential boundary; authoritative local source; high confidence.
|
|
9
|
+
- `packages/junior-github/src/plugin.ts` — local GitHub egress boundary; authoritative local source; high confidence.
|
|
9
10
|
- `packages/junior-github/skills/github-code/SKILL.md` — local repository targeting and credential guidance; authoritative local convention; high confidence.
|
|
10
11
|
- GitHub REST API documentation — no documented user-attachment upload operation found; official source; medium confidence for absence.
|
|
11
12
|
|