@sentry/junior-github 0.113.0 → 0.115.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/dist/credential-support.d.ts +90 -0
- package/dist/git-config.d.ts +21 -0
- package/dist/index.d.ts +1 -39
- package/dist/index.js +132 -100
- package/dist/outcomes/cost.d.ts +6 -0
- package/dist/plugin.d.ts +44 -0
- package/package.json +2 -2
- package/skills/attach-github-assets/SOURCES.md +2 -1
|
@@ -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 {};
|
|
@@ -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
|
|
@@ -1828,10 +1827,14 @@ var costWindowSchema = z10.object({
|
|
|
1828
1827
|
}));
|
|
1829
1828
|
var repositoryCostSchema = z10.object({
|
|
1830
1829
|
issueCostUsd: z10.number().nonnegative().nullable(),
|
|
1830
|
+
medianIssueCostUsd: z10.number().nonnegative().nullable(),
|
|
1831
|
+
medianPullRequestCostUsd: z10.number().nonnegative().nullable(),
|
|
1831
1832
|
pullRequestCostUsd: z10.number().nonnegative().nullable(),
|
|
1832
1833
|
repository: z10.string().min(1)
|
|
1833
1834
|
}).strict().transform((row) => ({
|
|
1834
1835
|
issueCostUsd: row.issueCostUsd ?? void 0,
|
|
1836
|
+
medianIssueCostUsd: row.medianIssueCostUsd ?? void 0,
|
|
1837
|
+
medianPullRequestCostUsd: row.medianPullRequestCostUsd ?? void 0,
|
|
1835
1838
|
pullRequestCostUsd: row.pullRequestCostUsd ?? void 0,
|
|
1836
1839
|
repository: row.repository
|
|
1837
1840
|
}));
|
|
@@ -2048,7 +2051,8 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2048
2051
|
WITH pull_request_entities AS (
|
|
2049
2052
|
SELECT
|
|
2050
2053
|
${pullRequests.repositoryFullName} AS repository,
|
|
2051
|
-
conversation_ids.ids AS ids
|
|
2054
|
+
conversation_ids.ids AS ids,
|
|
2055
|
+
${conversationTreeCost} AS cost_usd
|
|
2052
2056
|
FROM ${pullRequests}
|
|
2053
2057
|
CROSS JOIN LATERAL (
|
|
2054
2058
|
SELECT ${pullRequestConversationIds} AS ids
|
|
@@ -2057,7 +2061,8 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2057
2061
|
), issue_entities AS (
|
|
2058
2062
|
SELECT
|
|
2059
2063
|
${issues.repositoryFullName} AS repository,
|
|
2060
|
-
conversation_ids.ids AS ids
|
|
2064
|
+
conversation_ids.ids AS ids,
|
|
2065
|
+
${conversationTreeCost} AS cost_usd
|
|
2061
2066
|
FROM ${issues}
|
|
2062
2067
|
CROSS JOIN LATERAL (
|
|
2063
2068
|
SELECT ${issueConversationIds} AS ids
|
|
@@ -2079,7 +2084,15 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2079
2084
|
WHERE pull_request_entities.repository = repositories.repository
|
|
2080
2085
|
) AS ids
|
|
2081
2086
|
) AS conversation_ids
|
|
2082
|
-
), 0)::double precision AS pull_request_cost_usd
|
|
2087
|
+
), 0)::double precision AS pull_request_cost_usd,
|
|
2088
|
+
(
|
|
2089
|
+
SELECT percentile_cont(0.5) WITHIN GROUP (
|
|
2090
|
+
ORDER BY pull_request_entities.cost_usd
|
|
2091
|
+
)
|
|
2092
|
+
FROM pull_request_entities
|
|
2093
|
+
WHERE pull_request_entities.repository = repositories.repository
|
|
2094
|
+
AND pull_request_entities.cost_usd > 0
|
|
2095
|
+
)::double precision AS median_pull_request_cost_usd
|
|
2083
2096
|
FROM repositories
|
|
2084
2097
|
), issue_totals AS (
|
|
2085
2098
|
SELECT
|
|
@@ -2093,21 +2106,32 @@ async function aggregateGitHubRepositoryCosts(args) {
|
|
|
2093
2106
|
WHERE issue_entities.repository = repositories.repository
|
|
2094
2107
|
) AS ids
|
|
2095
2108
|
) AS conversation_ids
|
|
2096
|
-
), 0)::double precision AS issue_cost_usd
|
|
2109
|
+
), 0)::double precision AS issue_cost_usd,
|
|
2110
|
+
(
|
|
2111
|
+
SELECT percentile_cont(0.5) WITHIN GROUP (
|
|
2112
|
+
ORDER BY issue_entities.cost_usd
|
|
2113
|
+
)
|
|
2114
|
+
FROM issue_entities
|
|
2115
|
+
WHERE issue_entities.repository = repositories.repository
|
|
2116
|
+
AND issue_entities.cost_usd > 0
|
|
2117
|
+
)::double precision AS median_issue_cost_usd
|
|
2097
2118
|
FROM repositories
|
|
2098
2119
|
)
|
|
2099
2120
|
SELECT
|
|
2100
2121
|
repositories.repository AS "repository",
|
|
2101
2122
|
coalesce(pull_request_totals.pull_request_cost_usd, 0)::double precision
|
|
2102
2123
|
AS "pullRequestCostUsd",
|
|
2124
|
+
pull_request_totals.median_pull_request_cost_usd
|
|
2125
|
+
AS "medianPullRequestCostUsd",
|
|
2103
2126
|
coalesce(issue_totals.issue_cost_usd, 0)::double precision
|
|
2104
|
-
AS "issueCostUsd"
|
|
2127
|
+
AS "issueCostUsd",
|
|
2128
|
+
issue_totals.median_issue_cost_usd AS "medianIssueCostUsd"
|
|
2105
2129
|
FROM repositories
|
|
2106
2130
|
LEFT JOIN pull_request_totals
|
|
2107
2131
|
ON pull_request_totals.repository = repositories.repository
|
|
2108
2132
|
LEFT JOIN issue_totals
|
|
2109
2133
|
ON issue_totals.repository = repositories.repository
|
|
2110
|
-
ORDER BY "
|
|
2134
|
+
ORDER BY "repository" ASC
|
|
2111
2135
|
`);
|
|
2112
2136
|
return z10.array(repositoryCostSchema).parse(queryRows(result));
|
|
2113
2137
|
}
|
|
@@ -2548,7 +2572,7 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2548
2572
|
{ key: "merged", label: "Merged" },
|
|
2549
2573
|
{ key: "closed", label: "Closed unmerged" },
|
|
2550
2574
|
{ key: "mergeRate", label: "Closure merge rate" },
|
|
2551
|
-
{ key: "
|
|
2575
|
+
{ key: "medianCost", label: "Median cost" }
|
|
2552
2576
|
],
|
|
2553
2577
|
records: repositories.map(({ repository, ...stats }) => ({
|
|
2554
2578
|
id: repository,
|
|
@@ -2559,8 +2583,8 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2559
2583
|
closed: String(stats.closed),
|
|
2560
2584
|
juniorOnly: String(stats.juniorOnly),
|
|
2561
2585
|
mergeRate: formatPercent(stats.mergeRate),
|
|
2562
|
-
|
|
2563
|
-
repositoryCostByName.get(repository)?.
|
|
2586
|
+
medianCost: formatCostUsd(
|
|
2587
|
+
repositoryCostByName.get(repository)?.medianPullRequestCostUsd
|
|
2564
2588
|
)
|
|
2565
2589
|
}
|
|
2566
2590
|
}))
|
|
@@ -2575,7 +2599,7 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2575
2599
|
{ key: "duplicate", label: "Duplicate" },
|
|
2576
2600
|
{ key: "notPlanned", label: "Not planned" },
|
|
2577
2601
|
{ key: "unknown", label: "Unknown reason" },
|
|
2578
|
-
{ key: "
|
|
2602
|
+
{ key: "medianCost", label: "Median cost" }
|
|
2579
2603
|
],
|
|
2580
2604
|
records: issueRepositories.map(({ repository, ...stats }) => ({
|
|
2581
2605
|
id: repository,
|
|
@@ -2586,8 +2610,8 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2586
2610
|
duplicate: String(stats.closedDuplicate),
|
|
2587
2611
|
notPlanned: String(stats.closedNotPlanned),
|
|
2588
2612
|
unknown: String(stats.closedUnknown),
|
|
2589
|
-
|
|
2590
|
-
repositoryCostByName.get(repository)?.
|
|
2613
|
+
medianCost: formatCostUsd(
|
|
2614
|
+
repositoryCostByName.get(repository)?.medianIssueCostUsd
|
|
2591
2615
|
)
|
|
2592
2616
|
}
|
|
2593
2617
|
}))
|
|
@@ -2649,87 +2673,7 @@ async function classifyGitHubPullRequestCommitComposition(args) {
|
|
|
2649
2673
|
return foundCommit ? "junior_only" : void 0;
|
|
2650
2674
|
}
|
|
2651
2675
|
|
|
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
|
-
}
|
|
2676
|
+
// src/git-config.ts
|
|
2733
2677
|
function cleanIdentityPart(value) {
|
|
2734
2678
|
return String(value ?? "").replaceAll("\n", " ").replaceAll("\r", " ").replace(/[<>]/g, "").trim();
|
|
2735
2679
|
}
|
|
@@ -2937,6 +2881,92 @@ async function configureGit(ctx, key, value) {
|
|
|
2937
2881
|
);
|
|
2938
2882
|
}
|
|
2939
2883
|
}
|
|
2884
|
+
|
|
2885
|
+
// src/credential-support.ts
|
|
2886
|
+
import { createPrivateKey, createSign } from "crypto";
|
|
2887
|
+
var GITHUB_APP_ID_ENV = "GITHUB_APP_ID";
|
|
2888
|
+
var GITHUB_APP_PRIVATE_KEY_ENV = "GITHUB_APP_PRIVATE_KEY";
|
|
2889
|
+
var GITHUB_INSTALLATION_ID_ENV = "GITHUB_INSTALLATION_ID";
|
|
2890
|
+
var GITHUB_AUTH_TOKEN_ENV = "GITHUB_TOKEN";
|
|
2891
|
+
var GITHUB_AUTH_TOKEN_PLACEHOLDER = "ghp_host_managed_credential";
|
|
2892
|
+
var MAX_LEASE_MS = 60 * 60 * 1e3;
|
|
2893
|
+
var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
2894
|
+
var USER_REFRESH_TIMEOUT_MS = 2e4;
|
|
2895
|
+
var GITHUB_GRAPHQL_RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
2896
|
+
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
2897
|
+
var USER_TOKEN_GRANTS = /* @__PURE__ */ new Set(["user-read", "user-write"]);
|
|
2898
|
+
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.";
|
|
2899
|
+
var USER_WRITE_REQUIREMENTS = [
|
|
2900
|
+
"requesting GitHub user permission to perform this operation"
|
|
2901
|
+
];
|
|
2902
|
+
var GITHUB_CREDENTIAL_DOMAINS = ["api.github.com", "github.com"];
|
|
2903
|
+
var GITHUB_ASSET_UPLOAD_CREDENTIAL_DOMAINS = [
|
|
2904
|
+
...GITHUB_CREDENTIAL_DOMAINS,
|
|
2905
|
+
"uploads.github.com"
|
|
2906
|
+
];
|
|
2907
|
+
var GitHubUserRefreshRejectedError = class extends Error {
|
|
2908
|
+
constructor(message) {
|
|
2909
|
+
super(message);
|
|
2910
|
+
this.name = "GitHubUserRefreshRejectedError";
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
var GitHubRequestError = class extends Error {
|
|
2914
|
+
status;
|
|
2915
|
+
constructor(message, status) {
|
|
2916
|
+
super(message);
|
|
2917
|
+
this.name = "GitHubRequestError";
|
|
2918
|
+
this.status = status;
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2921
|
+
var GitHubPluginSetupError = class extends Error {
|
|
2922
|
+
constructor(message) {
|
|
2923
|
+
super(message);
|
|
2924
|
+
this.name = "GitHubPluginSetupError";
|
|
2925
|
+
}
|
|
2926
|
+
};
|
|
2927
|
+
function isRecord(value) {
|
|
2928
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2929
|
+
}
|
|
2930
|
+
function readEnv(name) {
|
|
2931
|
+
const value = process.env[name];
|
|
2932
|
+
if (typeof value !== "string") {
|
|
2933
|
+
return void 0;
|
|
2934
|
+
}
|
|
2935
|
+
const trimmed = value.trim();
|
|
2936
|
+
return trimmed ? trimmed : void 0;
|
|
2937
|
+
}
|
|
2938
|
+
function requireEnv(name) {
|
|
2939
|
+
const value = readEnv(name);
|
|
2940
|
+
if (!value) {
|
|
2941
|
+
throw new GitHubPluginSetupError(`Missing ${name}`);
|
|
2942
|
+
}
|
|
2943
|
+
return value;
|
|
2944
|
+
}
|
|
2945
|
+
function normalizeScopeList(scopes) {
|
|
2946
|
+
return [
|
|
2947
|
+
...new Set(
|
|
2948
|
+
(scopes ?? []).flatMap((scope) => String(scope).split(/\s+/)).map((scope) => scope.trim()).filter(Boolean)
|
|
2949
|
+
)
|
|
2950
|
+
].sort();
|
|
2951
|
+
}
|
|
2952
|
+
function normalizeOAuthScope(scope) {
|
|
2953
|
+
const normalized = normalizeScopeList(scope ? [scope] : []);
|
|
2954
|
+
return normalized.length ? normalized.join(" ") : void 0;
|
|
2955
|
+
}
|
|
2956
|
+
function hasRequiredOAuthScope(storedScope, requiredScope) {
|
|
2957
|
+
const required = normalizeScopeList(requiredScope ? [requiredScope] : []);
|
|
2958
|
+
if (required.length === 0) {
|
|
2959
|
+
return true;
|
|
2960
|
+
}
|
|
2961
|
+
const stored = new Set(normalizeScopeList(storedScope ? [storedScope] : []));
|
|
2962
|
+
if (stored.size === 0) {
|
|
2963
|
+
return false;
|
|
2964
|
+
}
|
|
2965
|
+
return required.every((scope) => stored.has(scope));
|
|
2966
|
+
}
|
|
2967
|
+
function isGitHubApiUrl(upstreamUrl) {
|
|
2968
|
+
return upstreamUrl.hostname.toLowerCase() === "api.github.com";
|
|
2969
|
+
}
|
|
2940
2970
|
function base64Url(input) {
|
|
2941
2971
|
return Buffer.from(input).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
2942
2972
|
}
|
|
@@ -3437,6 +3467,8 @@ function createPermissionCache() {
|
|
|
3437
3467
|
return await pending;
|
|
3438
3468
|
};
|
|
3439
3469
|
}
|
|
3470
|
+
|
|
3471
|
+
// src/plugin.ts
|
|
3440
3472
|
function githubSmartHttpAccess(upstreamUrl) {
|
|
3441
3473
|
const pathname = upstreamUrl.pathname.toLowerCase();
|
|
3442
3474
|
const service = upstreamUrl.searchParams.get("service")?.toLowerCase();
|
|
@@ -3455,14 +3487,14 @@ function githubSmartHttpAccess(upstreamUrl) {
|
|
|
3455
3487
|
function isGitHubGraphqlUrl(upstreamUrl) {
|
|
3456
3488
|
return upstreamUrl.hostname.toLowerCase() === "api.github.com" && upstreamUrl.pathname.toLowerCase().endsWith("/graphql");
|
|
3457
3489
|
}
|
|
3458
|
-
function
|
|
3490
|
+
function isGitHubApiUrl2(upstreamUrl) {
|
|
3459
3491
|
return upstreamUrl.hostname.toLowerCase() === "api.github.com";
|
|
3460
3492
|
}
|
|
3461
3493
|
function isGitHubAssetUploadRequest(method, upstreamUrl) {
|
|
3462
3494
|
return method === "POST" && upstreamUrl.hostname.toLowerCase() === "uploads.github.com" && upstreamUrl.pathname === "/user-attachments/assets";
|
|
3463
3495
|
}
|
|
3464
3496
|
function githubUserReadReason(method, upstreamUrl) {
|
|
3465
|
-
if (method !== "GET" || !
|
|
3497
|
+
if (method !== "GET" || !isGitHubApiUrl2(upstreamUrl)) {
|
|
3466
3498
|
return void 0;
|
|
3467
3499
|
}
|
|
3468
3500
|
return upstreamUrl.pathname.toLowerCase() === "/user" ? "github.user-read" : void 0;
|
|
@@ -3590,7 +3622,7 @@ function shouldInspectGitHubGraphqlResponse(ctx) {
|
|
|
3590
3622
|
}
|
|
3591
3623
|
function githubApiWriteGrantName(method, upstreamUrl) {
|
|
3592
3624
|
const pathname = upstreamUrl.pathname.toLowerCase();
|
|
3593
|
-
if (!
|
|
3625
|
+
if (!isGitHubApiUrl2(upstreamUrl)) {
|
|
3594
3626
|
return void 0;
|
|
3595
3627
|
}
|
|
3596
3628
|
if (method === "POST" && /^\/repos\/[^/]+\/[^/]+\/actions\/workflows\/[^/]+\/dispatches$/.test(
|
|
@@ -3632,10 +3664,10 @@ function githubApiWriteGrantName(method, upstreamUrl) {
|
|
|
3632
3664
|
return void 0;
|
|
3633
3665
|
}
|
|
3634
3666
|
function isGitHubIssueCreateRestRequest(method, upstreamUrl) {
|
|
3635
|
-
return method === "POST" &&
|
|
3667
|
+
return method === "POST" && isGitHubApiUrl2(upstreamUrl) && /^\/repos\/[^/]+\/[^/]+\/issues$/.test(upstreamUrl.pathname.toLowerCase());
|
|
3636
3668
|
}
|
|
3637
3669
|
function isGitHubPullCreateRestRequest(method, upstreamUrl) {
|
|
3638
|
-
return method === "POST" &&
|
|
3670
|
+
return method === "POST" && isGitHubApiUrl2(upstreamUrl) && /^\/repos\/[^/]+\/[^/]+\/pulls$/.test(upstreamUrl.pathname.toLowerCase());
|
|
3639
3671
|
}
|
|
3640
3672
|
function isGitHubIssueCreateGraphqlMutation(method, upstreamUrl, bodyText) {
|
|
3641
3673
|
if (method !== "POST" || !isGitHubGraphqlUrl(upstreamUrl)) {
|
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/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 expose as capabilities and downscope
|
|
20
|
+
* to read for installation-read tokens.
|
|
21
|
+
*
|
|
22
|
+
* Keys may use GitHub permission names with underscores or hyphens. Junior
|
|
23
|
+
* records these as plugin capabilities. Installation-write tokens inherit
|
|
24
|
+
* the App installation's complete permission envelope.
|
|
25
|
+
* GitHub remains the source of truth for whether a 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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.115.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.115.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
|
|