@inkeep/agents-work-apps 0.0.0-dev-20260203033642
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/LICENSE.md +49 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.js +3 -0
- package/dist/db/runDbClient.d.ts +6 -0
- package/dist/db/runDbClient.js +9 -0
- package/dist/env.d.ts +47 -0
- package/dist/env.js +48 -0
- package/dist/github/config.d.ts +22 -0
- package/dist/github/config.js +79 -0
- package/dist/github/index.d.ts +13 -0
- package/dist/github/index.js +23 -0
- package/dist/github/installation.d.ts +66 -0
- package/dist/github/installation.js +293 -0
- package/dist/github/jwks.d.ts +20 -0
- package/dist/github/jwks.js +85 -0
- package/dist/github/mcp/auth.d.ts +10 -0
- package/dist/github/mcp/auth.js +43 -0
- package/dist/github/mcp/index.d.ts +11 -0
- package/dist/github/mcp/index.js +670 -0
- package/dist/github/mcp/schemas.d.ts +87 -0
- package/dist/github/mcp/schemas.js +69 -0
- package/dist/github/mcp/utils.d.ts +228 -0
- package/dist/github/mcp/utils.js +464 -0
- package/dist/github/oidcToken.d.ts +22 -0
- package/dist/github/oidcToken.js +140 -0
- package/dist/github/routes/setup.d.ts +7 -0
- package/dist/github/routes/setup.js +217 -0
- package/dist/github/routes/tokenExchange.d.ts +7 -0
- package/dist/github/routes/tokenExchange.js +233 -0
- package/dist/github/routes/webhooks.d.ts +12 -0
- package/dist/github/routes/webhooks.js +278 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +3 -0
- package/package.json +65 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { env } from "../../env.js";
|
|
2
|
+
import { getLogger } from "../../logger.js";
|
|
3
|
+
import runDbClient_default from "../../db/runDbClient.js";
|
|
4
|
+
import { getStateSigningSecret, isStateSigningConfigured } from "../config.js";
|
|
5
|
+
import { createAppJwt, determineStatus, fetchInstallationDetails, fetchInstallationRepositories } from "../installation.js";
|
|
6
|
+
import { createInstallation, generateId, getInstallationByGitHubId, listProjectsMetadata, setProjectAccessMode, syncRepositories, updateInstallationStatusByGitHubId } from "@inkeep/agents-core";
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
import { jwtVerify } from "jose";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
//#region src/github/routes/setup.ts
|
|
12
|
+
const logger = getLogger("github-setup");
|
|
13
|
+
const STATE_JWT_ISSUER = "inkeep-agents-api";
|
|
14
|
+
const STATE_JWT_AUDIENCE = "github-app-install";
|
|
15
|
+
const CallbackQuerySchema = z.object({
|
|
16
|
+
installation_id: z.string(),
|
|
17
|
+
setup_action: z.enum([
|
|
18
|
+
"install",
|
|
19
|
+
"update",
|
|
20
|
+
"request"
|
|
21
|
+
]),
|
|
22
|
+
state: z.string()
|
|
23
|
+
});
|
|
24
|
+
function getManageUiUrl() {
|
|
25
|
+
return env.INKEEP_AGENTS_MANAGE_UI_URL || "http://localhost:3000";
|
|
26
|
+
}
|
|
27
|
+
function buildErrorRedirectUrl(message) {
|
|
28
|
+
const baseUrl = getManageUiUrl();
|
|
29
|
+
const url = new URL("/github/setup-error", baseUrl);
|
|
30
|
+
url.searchParams.set("message", message);
|
|
31
|
+
return url.toString();
|
|
32
|
+
}
|
|
33
|
+
function buildRedirectUrl(params) {
|
|
34
|
+
const baseUrl = getManageUiUrl();
|
|
35
|
+
const url = new URL(`/${params.tenantId}/work-apps/github`, baseUrl);
|
|
36
|
+
url.searchParams.set("status", params.status);
|
|
37
|
+
if (params.message) url.searchParams.set("message", params.message);
|
|
38
|
+
if (params.installationId) url.searchParams.set("installation_id", params.installationId);
|
|
39
|
+
return url.toString();
|
|
40
|
+
}
|
|
41
|
+
async function verifyStateToken(state) {
|
|
42
|
+
if (!isStateSigningConfigured()) return {
|
|
43
|
+
success: false,
|
|
44
|
+
error: "GitHub App installation is not configured"
|
|
45
|
+
};
|
|
46
|
+
const secret = getStateSigningSecret();
|
|
47
|
+
const secretKey = new TextEncoder().encode(secret);
|
|
48
|
+
try {
|
|
49
|
+
const { payload } = await jwtVerify(state, secretKey, {
|
|
50
|
+
issuer: STATE_JWT_ISSUER,
|
|
51
|
+
audience: STATE_JWT_AUDIENCE
|
|
52
|
+
});
|
|
53
|
+
const tenantId = payload.tenantId;
|
|
54
|
+
if (!tenantId) return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: "Invalid state: missing tenantId"
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
tenantId
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error instanceof Error) {
|
|
64
|
+
const errorMessage = error.message.toLowerCase();
|
|
65
|
+
const errorName = error.name.toLowerCase();
|
|
66
|
+
if (errorMessage.includes("expired") || errorName.includes("expired")) return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: "State token has expired. Please try installing again."
|
|
69
|
+
};
|
|
70
|
+
if (errorMessage.includes("signature")) return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: "Invalid state signature"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
logger.error({ error }, "Failed to verify state token");
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: "Invalid state token"
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const app = new Hono();
|
|
83
|
+
app.get("/", async (c) => {
|
|
84
|
+
const queryParams = {
|
|
85
|
+
installation_id: c.req.query("installation_id"),
|
|
86
|
+
setup_action: c.req.query("setup_action"),
|
|
87
|
+
state: c.req.query("state")
|
|
88
|
+
};
|
|
89
|
+
const parseResult = CallbackQuerySchema.safeParse(queryParams);
|
|
90
|
+
if (!parseResult.success) {
|
|
91
|
+
logger.warn({ errors: parseResult.error.issues }, "Invalid callback parameters");
|
|
92
|
+
return c.redirect(buildErrorRedirectUrl("Invalid callback parameters"));
|
|
93
|
+
}
|
|
94
|
+
const { installation_id, setup_action, state } = parseResult.data;
|
|
95
|
+
logger.info({
|
|
96
|
+
installation_id,
|
|
97
|
+
setup_action
|
|
98
|
+
}, "Processing GitHub callback");
|
|
99
|
+
const stateResult = await verifyStateToken(state);
|
|
100
|
+
if (!stateResult.success) {
|
|
101
|
+
logger.warn({ error: stateResult.error }, "State verification failed");
|
|
102
|
+
return c.redirect(buildErrorRedirectUrl(stateResult.error));
|
|
103
|
+
}
|
|
104
|
+
const { tenantId } = stateResult;
|
|
105
|
+
logger.info({
|
|
106
|
+
tenantId,
|
|
107
|
+
installation_id
|
|
108
|
+
}, "State verified successfully");
|
|
109
|
+
let appJwt;
|
|
110
|
+
try {
|
|
111
|
+
appJwt = await createAppJwt();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
logger.error({ error }, "Failed to create GitHub App JWT");
|
|
114
|
+
return c.redirect(buildErrorRedirectUrl("GitHub App not configured properly"));
|
|
115
|
+
}
|
|
116
|
+
const installationResult = await fetchInstallationDetails(installation_id, appJwt);
|
|
117
|
+
if (!installationResult.success) return c.redirect(buildRedirectUrl({
|
|
118
|
+
tenantId,
|
|
119
|
+
status: "error",
|
|
120
|
+
message: "Failed to verify installation with GitHub"
|
|
121
|
+
}));
|
|
122
|
+
const { installation } = installationResult;
|
|
123
|
+
logger.info({
|
|
124
|
+
accountLogin: installation.account.login,
|
|
125
|
+
accountType: installation.account.type,
|
|
126
|
+
installationId: installation.id
|
|
127
|
+
}, "Fetched installation details from GitHub");
|
|
128
|
+
const reposResult = await fetchInstallationRepositories(installation_id, appJwt);
|
|
129
|
+
if (!reposResult.success) logger.warn({ error: reposResult.error }, "Failed to fetch repositories, continuing with empty list");
|
|
130
|
+
const repositories = reposResult.success ? reposResult.repositories : [];
|
|
131
|
+
logger.info({ repositoryCount: repositories.length }, "Fetched repositories from GitHub");
|
|
132
|
+
const status = determineStatus(setup_action);
|
|
133
|
+
try {
|
|
134
|
+
const existingInstallation = await getInstallationByGitHubId(runDbClient_default)(installation_id);
|
|
135
|
+
let internalInstallationId;
|
|
136
|
+
if (existingInstallation) {
|
|
137
|
+
logger.info({
|
|
138
|
+
existingId: existingInstallation.id,
|
|
139
|
+
setup_action
|
|
140
|
+
}, "Updating existing installation");
|
|
141
|
+
const updated = await updateInstallationStatusByGitHubId(runDbClient_default)({
|
|
142
|
+
gitHubInstallationId: installation_id,
|
|
143
|
+
status
|
|
144
|
+
});
|
|
145
|
+
if (!updated) throw new Error("Failed to update installation status");
|
|
146
|
+
internalInstallationId = updated.id;
|
|
147
|
+
} else {
|
|
148
|
+
logger.info({
|
|
149
|
+
tenantId,
|
|
150
|
+
setup_action
|
|
151
|
+
}, "Creating new installation record");
|
|
152
|
+
internalInstallationId = (await createInstallation(runDbClient_default)({
|
|
153
|
+
id: generateId(),
|
|
154
|
+
tenantId,
|
|
155
|
+
installationId: installation_id,
|
|
156
|
+
accountLogin: installation.account.login,
|
|
157
|
+
accountId: String(installation.account.id),
|
|
158
|
+
accountType: installation.account.type,
|
|
159
|
+
status
|
|
160
|
+
})).id;
|
|
161
|
+
const projectsInTenant = await listProjectsMetadata(runDbClient_default)({ tenantId });
|
|
162
|
+
if (projectsInTenant.length > 0) {
|
|
163
|
+
logger.info({
|
|
164
|
+
tenantId,
|
|
165
|
+
projectCount: projectsInTenant.length
|
|
166
|
+
}, "Setting GitHub access mode to \"all\" for all existing projects");
|
|
167
|
+
await Promise.all(projectsInTenant.map((project) => setProjectAccessMode(runDbClient_default)({
|
|
168
|
+
tenantId,
|
|
169
|
+
projectId: project.id,
|
|
170
|
+
mode: "all"
|
|
171
|
+
})));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (repositories.length > 0) {
|
|
175
|
+
const syncResult = await syncRepositories(runDbClient_default)({
|
|
176
|
+
installationId: internalInstallationId,
|
|
177
|
+
repositories: repositories.map((repo) => ({
|
|
178
|
+
repositoryId: String(repo.id),
|
|
179
|
+
repositoryName: repo.name,
|
|
180
|
+
repositoryFullName: repo.full_name,
|
|
181
|
+
private: repo.private
|
|
182
|
+
}))
|
|
183
|
+
});
|
|
184
|
+
logger.info({
|
|
185
|
+
added: syncResult.added,
|
|
186
|
+
removed: syncResult.removed,
|
|
187
|
+
updated: syncResult.updated
|
|
188
|
+
}, "Synced repositories");
|
|
189
|
+
}
|
|
190
|
+
logger.info({
|
|
191
|
+
tenantId,
|
|
192
|
+
installationId: installation_id,
|
|
193
|
+
accountLogin: installation.account.login,
|
|
194
|
+
status
|
|
195
|
+
}, "GitHub App installation processed successfully");
|
|
196
|
+
return c.redirect(buildRedirectUrl({
|
|
197
|
+
tenantId,
|
|
198
|
+
status: "success",
|
|
199
|
+
installationId: internalInstallationId
|
|
200
|
+
}));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
logger.error({
|
|
203
|
+
error,
|
|
204
|
+
tenantId,
|
|
205
|
+
installation_id
|
|
206
|
+
}, "Failed to store installation in database");
|
|
207
|
+
return c.redirect(buildRedirectUrl({
|
|
208
|
+
tenantId,
|
|
209
|
+
status: "error",
|
|
210
|
+
message: "Failed to complete installation setup"
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
var setup_default = app;
|
|
215
|
+
|
|
216
|
+
//#endregion
|
|
217
|
+
export { setup_default as default };
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { getLogger } from "../../logger.js";
|
|
2
|
+
import runDbClient_default from "../../db/runDbClient.js";
|
|
3
|
+
import { isGitHubAppConfigured } from "../config.js";
|
|
4
|
+
import { generateInstallationAccessToken, lookupInstallationForRepo } from "../installation.js";
|
|
5
|
+
import { validateOidcToken } from "../oidcToken.js";
|
|
6
|
+
import { checkProjectRepositoryAccess, getInstallationByGitHubId } from "@inkeep/agents-core";
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
//#region src/github/routes/tokenExchange.ts
|
|
11
|
+
const logger = getLogger("github-token-exchange");
|
|
12
|
+
const TokenExchangeRequestSchema = z.object({
|
|
13
|
+
oidc_token: z.string(),
|
|
14
|
+
project_id: z.string().optional()
|
|
15
|
+
});
|
|
16
|
+
const app = new Hono();
|
|
17
|
+
/**
|
|
18
|
+
* Exchange GitHub OIDC token for installation token.
|
|
19
|
+
*
|
|
20
|
+
* This is an internal infrastructure endpoint called by the CLI from GitHub Actions.
|
|
21
|
+
* It exchanges a GitHub Actions OIDC token for a GitHub App installation access token.
|
|
22
|
+
* Not included in the public OpenAPI spec.
|
|
23
|
+
*/
|
|
24
|
+
app.post("/", async (c) => {
|
|
25
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
26
|
+
const parseResult = TokenExchangeRequestSchema.safeParse(rawBody);
|
|
27
|
+
if (!parseResult.success) {
|
|
28
|
+
const errorMessage = parseResult.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
29
|
+
c.header("Content-Type", "application/problem+json");
|
|
30
|
+
return c.json({
|
|
31
|
+
title: "Bad Request",
|
|
32
|
+
status: 400,
|
|
33
|
+
detail: errorMessage,
|
|
34
|
+
error: errorMessage
|
|
35
|
+
}, 400);
|
|
36
|
+
}
|
|
37
|
+
const body = parseResult.data;
|
|
38
|
+
logger.info({}, "Processing token exchange request");
|
|
39
|
+
if (!isGitHubAppConfigured()) {
|
|
40
|
+
logger.error({}, "GitHub App credentials not configured");
|
|
41
|
+
const errorMessage = "GitHub App credentials are not configured. Please contact the administrator to set up GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY.";
|
|
42
|
+
c.header("Content-Type", "application/problem+json");
|
|
43
|
+
return c.json({
|
|
44
|
+
title: "GitHub App Not Configured",
|
|
45
|
+
status: 500,
|
|
46
|
+
detail: errorMessage,
|
|
47
|
+
error: errorMessage
|
|
48
|
+
}, 500);
|
|
49
|
+
}
|
|
50
|
+
const validationResult = await validateOidcToken(body.oidc_token);
|
|
51
|
+
if (!validationResult.success) {
|
|
52
|
+
const errorType = validationResult.errorType;
|
|
53
|
+
logger.warn({
|
|
54
|
+
errorType,
|
|
55
|
+
message: validationResult.message
|
|
56
|
+
}, "OIDC token validation failed");
|
|
57
|
+
c.header("Content-Type", "application/problem+json");
|
|
58
|
+
if (errorType === "malformed") return c.json({
|
|
59
|
+
title: "Bad Request",
|
|
60
|
+
status: 400,
|
|
61
|
+
detail: validationResult.message,
|
|
62
|
+
error: validationResult.message
|
|
63
|
+
}, 400);
|
|
64
|
+
return c.json({
|
|
65
|
+
title: "Token Validation Failed",
|
|
66
|
+
status: 401,
|
|
67
|
+
detail: validationResult.message,
|
|
68
|
+
error: validationResult.message
|
|
69
|
+
}, 401);
|
|
70
|
+
}
|
|
71
|
+
const { claims } = validationResult;
|
|
72
|
+
const installationResult = await lookupInstallationForRepo(claims.repository_owner, claims.repository.split("/")[1]);
|
|
73
|
+
if (!installationResult.success) {
|
|
74
|
+
const { errorType, message } = installationResult;
|
|
75
|
+
if (errorType === "not_installed") {
|
|
76
|
+
c.header("Content-Type", "application/problem+json");
|
|
77
|
+
return c.json({
|
|
78
|
+
title: "GitHub App Not Installed",
|
|
79
|
+
status: 403,
|
|
80
|
+
detail: message,
|
|
81
|
+
error: message
|
|
82
|
+
}, 403);
|
|
83
|
+
}
|
|
84
|
+
logger.error({
|
|
85
|
+
errorType,
|
|
86
|
+
message,
|
|
87
|
+
repository: claims.repository
|
|
88
|
+
}, "Failed to look up GitHub App installation");
|
|
89
|
+
c.header("Content-Type", "application/problem+json");
|
|
90
|
+
return c.json({
|
|
91
|
+
title: "Installation Lookup Failed",
|
|
92
|
+
status: 500,
|
|
93
|
+
detail: message,
|
|
94
|
+
error: message
|
|
95
|
+
}, 500);
|
|
96
|
+
}
|
|
97
|
+
const { installation } = installationResult;
|
|
98
|
+
logger.info({
|
|
99
|
+
installationId: installation.installationId,
|
|
100
|
+
repository: claims.repository
|
|
101
|
+
}, "Found GitHub App installation");
|
|
102
|
+
const dbInstallation = await getInstallationByGitHubId(runDbClient_default)(installation.installationId.toString());
|
|
103
|
+
if (!dbInstallation) {
|
|
104
|
+
const errorMessage = "GitHub App installation not registered. Please connect your GitHub organization in the Inkeep dashboard.";
|
|
105
|
+
logger.warn({
|
|
106
|
+
installationId: installation.installationId,
|
|
107
|
+
repository: claims.repository
|
|
108
|
+
}, "Installation not found in database");
|
|
109
|
+
c.header("Content-Type", "application/problem+json");
|
|
110
|
+
return c.json({
|
|
111
|
+
title: "Installation Not Registered",
|
|
112
|
+
status: 403,
|
|
113
|
+
detail: errorMessage,
|
|
114
|
+
error: errorMessage
|
|
115
|
+
}, 403);
|
|
116
|
+
}
|
|
117
|
+
if (dbInstallation.status === "pending") {
|
|
118
|
+
const errorMessage = "GitHub App installation is pending organization admin approval";
|
|
119
|
+
logger.warn({
|
|
120
|
+
installationId: installation.installationId,
|
|
121
|
+
repository: claims.repository,
|
|
122
|
+
status: dbInstallation.status
|
|
123
|
+
}, "Installation is pending approval");
|
|
124
|
+
c.header("Content-Type", "application/problem+json");
|
|
125
|
+
return c.json({
|
|
126
|
+
title: "Installation Pending",
|
|
127
|
+
status: 403,
|
|
128
|
+
detail: errorMessage,
|
|
129
|
+
error: errorMessage
|
|
130
|
+
}, 403);
|
|
131
|
+
}
|
|
132
|
+
if (dbInstallation.status === "suspended") {
|
|
133
|
+
const errorMessage = "GitHub App installation is suspended";
|
|
134
|
+
logger.warn({
|
|
135
|
+
installationId: installation.installationId,
|
|
136
|
+
repository: claims.repository,
|
|
137
|
+
status: dbInstallation.status
|
|
138
|
+
}, "Installation is suspended");
|
|
139
|
+
c.header("Content-Type", "application/problem+json");
|
|
140
|
+
return c.json({
|
|
141
|
+
title: "Installation Suspended",
|
|
142
|
+
status: 403,
|
|
143
|
+
detail: errorMessage,
|
|
144
|
+
error: errorMessage
|
|
145
|
+
}, 403);
|
|
146
|
+
}
|
|
147
|
+
if (dbInstallation.status === "deleted") {
|
|
148
|
+
const errorMessage = "GitHub App installation has been disconnected";
|
|
149
|
+
logger.warn({
|
|
150
|
+
installationId: installation.installationId,
|
|
151
|
+
repository: claims.repository,
|
|
152
|
+
status: dbInstallation.status
|
|
153
|
+
}, "Installation has been deleted");
|
|
154
|
+
c.header("Content-Type", "application/problem+json");
|
|
155
|
+
return c.json({
|
|
156
|
+
title: "Installation Disconnected",
|
|
157
|
+
status: 403,
|
|
158
|
+
detail: errorMessage,
|
|
159
|
+
error: errorMessage
|
|
160
|
+
}, 403);
|
|
161
|
+
}
|
|
162
|
+
logger.info({
|
|
163
|
+
installationId: installation.installationId,
|
|
164
|
+
tenantId: dbInstallation.tenantId,
|
|
165
|
+
repository: claims.repository
|
|
166
|
+
}, "Installation validated against database");
|
|
167
|
+
if (body.project_id) {
|
|
168
|
+
const accessCheck = await checkProjectRepositoryAccess(runDbClient_default)({
|
|
169
|
+
projectId: body.project_id,
|
|
170
|
+
repositoryFullName: claims.repository,
|
|
171
|
+
tenantId: dbInstallation.tenantId
|
|
172
|
+
});
|
|
173
|
+
if (!accessCheck.hasAccess) {
|
|
174
|
+
const errorMessage = `Project does not have access to repository ${claims.repository}. ${accessCheck.reason}`;
|
|
175
|
+
logger.warn({
|
|
176
|
+
installationId: installation.installationId,
|
|
177
|
+
tenantId: dbInstallation.tenantId,
|
|
178
|
+
projectId: body.project_id,
|
|
179
|
+
repository: claims.repository,
|
|
180
|
+
reason: accessCheck.reason
|
|
181
|
+
}, "Project does not have access to repository");
|
|
182
|
+
c.header("Content-Type", "application/problem+json");
|
|
183
|
+
return c.json({
|
|
184
|
+
title: "Repository Access Denied",
|
|
185
|
+
status: 403,
|
|
186
|
+
detail: errorMessage,
|
|
187
|
+
error: errorMessage
|
|
188
|
+
}, 403);
|
|
189
|
+
}
|
|
190
|
+
logger.info({
|
|
191
|
+
installationId: installation.installationId,
|
|
192
|
+
tenantId: dbInstallation.tenantId,
|
|
193
|
+
projectId: body.project_id,
|
|
194
|
+
repository: claims.repository,
|
|
195
|
+
reason: accessCheck.reason
|
|
196
|
+
}, "Project has access to repository");
|
|
197
|
+
}
|
|
198
|
+
const tokenResult = await generateInstallationAccessToken(installation.installationId);
|
|
199
|
+
if (!tokenResult.success) {
|
|
200
|
+
const { errorType, message } = tokenResult;
|
|
201
|
+
logger.error({
|
|
202
|
+
errorType,
|
|
203
|
+
message,
|
|
204
|
+
installationId: installation.installationId,
|
|
205
|
+
repository: claims.repository
|
|
206
|
+
}, "Failed to generate installation access token");
|
|
207
|
+
c.header("Content-Type", "application/problem+json");
|
|
208
|
+
return c.json({
|
|
209
|
+
title: "Token Generation Failed",
|
|
210
|
+
status: 500,
|
|
211
|
+
detail: message,
|
|
212
|
+
error: message
|
|
213
|
+
}, 500);
|
|
214
|
+
}
|
|
215
|
+
const { accessToken } = tokenResult;
|
|
216
|
+
logger.info({
|
|
217
|
+
installationId: installation.installationId,
|
|
218
|
+
tenantId: dbInstallation.tenantId,
|
|
219
|
+
repository: claims.repository,
|
|
220
|
+
expiresAt: accessToken.expiresAt
|
|
221
|
+
}, "Token exchange completed successfully");
|
|
222
|
+
return c.json({
|
|
223
|
+
token: accessToken.token,
|
|
224
|
+
expires_at: accessToken.expiresAt,
|
|
225
|
+
repository: claims.repository,
|
|
226
|
+
installation_id: installation.installationId,
|
|
227
|
+
tenant_id: dbInstallation.tenantId
|
|
228
|
+
}, 200);
|
|
229
|
+
});
|
|
230
|
+
var tokenExchange_default = app;
|
|
231
|
+
|
|
232
|
+
//#endregion
|
|
233
|
+
export { tokenExchange_default as default };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import * as hono_types4 from "hono/types";
|
|
3
|
+
|
|
4
|
+
//#region src/github/routes/webhooks.d.ts
|
|
5
|
+
interface WebhookVerificationResult {
|
|
6
|
+
success: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
declare function verifyWebhookSignature(payload: string, signature: string | undefined, secret: string): WebhookVerificationResult;
|
|
10
|
+
declare const app: Hono<hono_types4.BlankEnv, hono_types4.BlankSchema, "/">;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|