drbh commited on
Commit
6f5b644
·
1 Parent(s): 1b70735

feat: boilderplate app

Browse files
app/lib/github-app.server.ts ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { App } from "@octokit/app";
2
+ import { createAppAuth } from "@octokit/auth-app";
3
+ import jwt from "jsonwebtoken";
4
+
5
+ // GitHub App configuration - these should be environment variables in production
6
+ const GITHUB_APP_ID = process.env.GITHUB_APP_ID || "1356087";
7
+ const GITHUB_APP_PRIVATE_KEY = process.env.GITHUB_APP_PRIVATE_KEY || "your-private-key";
8
+ const GITHUB_APP_CLIENT_ID = process.env.GITHUB_APP_CLIENT_ID || "Iv23liFxEtiiREnjOeB2";
9
+ const GITHUB_APP_CLIENT_SECRET = process.env.GITHUB_APP_CLIENT_SECRET || "your-client-secret";
10
+
11
+ // For now, we'll hardcode a simple in-memory store
12
+ // In production, you'd use a database
13
+ const userAuthStore = new Map<string, any>();
14
+
15
+ export class GitHubAppAuth {
16
+ private app: App;
17
+
18
+ constructor() {
19
+ this.app = new App({
20
+ appId: GITHUB_APP_ID,
21
+ privateKey: GITHUB_APP_PRIVATE_KEY,
22
+ oauth: {
23
+ clientId: GITHUB_APP_CLIENT_ID,
24
+ clientSecret: GITHUB_APP_CLIENT_SECRET,
25
+ },
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Generate the installation URL for users to authorize the app
31
+ */
32
+ getInstallationUrl(state?: string): string {
33
+ const baseUrl = `https://github.com/apps/${process.env.GITHUB_APP_NAME || 'hugex-gh'}/installations/new`;
34
+ const params = new URLSearchParams();
35
+
36
+ if (state) {
37
+ params.append('state', state);
38
+ }
39
+
40
+ return `${baseUrl}?${params.toString()}`;
41
+ }
42
+
43
+ /**
44
+ * Get OAuth authorization URL for user identity
45
+ */
46
+ getOAuthUrl(state?: string): string {
47
+ const params = new URLSearchParams({
48
+ client_id: GITHUB_APP_CLIENT_ID,
49
+ redirect_uri: process.env.GITHUB_CALLBACK_URL || 'http://localhost:3000/auth/github/callback',
50
+ scope: 'user:email',
51
+ state: state || '',
52
+ });
53
+
54
+ return `https://github.com/login/oauth/authorize?${params.toString()}`;
55
+ }
56
+
57
+ /**
58
+ * Exchange code for access token and get user info
59
+ */
60
+ async handleCallback(code: string, state?: string) {
61
+ try {
62
+ const { data } = await this.app.oauth.createToken({
63
+ code,
64
+ });
65
+
66
+ const { token } = data;
67
+
68
+ // Get user information
69
+ const userOctokit = await this.app.oauth.getUserOctokit({
70
+ token,
71
+ });
72
+
73
+ const { data: user } = await userOctokit.rest.users.getAuthenticated();
74
+
75
+ // Store user auth info (in production, save to database)
76
+ const userAuth = {
77
+ id: user.id,
78
+ login: user.login,
79
+ name: user.name,
80
+ email: user.email,
81
+ avatar_url: user.avatar_url,
82
+ token,
83
+ authenticated_at: new Date().toISOString(),
84
+ state,
85
+ };
86
+
87
+ userAuthStore.set(user.login, userAuth);
88
+
89
+ return userAuth;
90
+ } catch (error) {
91
+ console.error('GitHub callback error:', error);
92
+ throw new Error('Failed to authenticate with GitHub');
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Get stored user authentication info
98
+ */
99
+ getUserAuth(login: string) {
100
+ return userAuthStore.get(login);
101
+ }
102
+
103
+ /**
104
+ * Get all stored user auths (for debugging)
105
+ */
106
+ getAllUserAuths() {
107
+ return Array.from(userAuthStore.values());
108
+ }
109
+
110
+ /**
111
+ * Create an authenticated Octokit instance for a user
112
+ */
113
+ async getUserOctokit(login: string) {
114
+ const userAuth = this.getUserAuth(login);
115
+ if (!userAuth) {
116
+ throw new Error(`No authentication found for user: ${login}`);
117
+ }
118
+
119
+ return await this.app.oauth.getUserOctokit({
120
+ token: userAuth.token,
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Get app installation for a repository
126
+ */
127
+ async getInstallationOctokit(installationId: number) {
128
+ return await this.app.getInstallationOctokit(installationId);
129
+ }
130
+
131
+ /**
132
+ * Verify webhook signature
133
+ */
134
+ verifyWebhookSignature(payload: string, signature: string): boolean {
135
+ const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET;
136
+ if (!webhookSecret) {
137
+ console.warn('GITHUB_WEBHOOK_SECRET not set, skipping signature verification');
138
+ return true;
139
+ }
140
+
141
+ const expectedSignature = `sha256=${require('crypto')
142
+ .createHmac('sha256', webhookSecret)
143
+ .update(payload, 'utf8')
144
+ .digest('hex')}`;
145
+
146
+ return signature === expectedSignature;
147
+ }
148
+ }
149
+
150
+ // Singleton instance
151
+ export const githubApp = new GitHubAppAuth();
app/lib/session.server.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createCookieSessionStorage } from "@remix-run/node";
2
+
3
+ const { getSession, commitSession, destroySession } = createCookieSessionStorage({
4
+ cookie: {
5
+ name: "__session",
6
+ httpOnly: true,
7
+ maxAge: 60 * 60 * 24 * 30, // 30 days
8
+ path: "/",
9
+ sameSite: "lax",
10
+ secrets: [process.env.SESSION_SECRET || "your-secret-key"],
11
+ secure: process.env.NODE_ENV === "production",
12
+ },
13
+ });
14
+
15
+ export { getSession, commitSession, destroySession };
16
+
17
+ export interface UserSession {
18
+ userId: string;
19
+ login: string;
20
+ name?: string;
21
+ email?: string;
22
+ avatar_url?: string;
23
+ }
24
+
25
+ export async function requireUserSession(request: Request): Promise<UserSession> {
26
+ const session = await getSession(request.headers.get("Cookie"));
27
+ const userSession = session.get("user");
28
+
29
+ if (!userSession) {
30
+ throw new Response("Unauthorized", { status: 401 });
31
+ }
32
+
33
+ return userSession;
34
+ }
35
+
36
+ export async function getUserSession(request: Request): Promise<UserSession | null> {
37
+ const session = await getSession(request.headers.get("Cookie"));
38
+ return session.get("user") || null;
39
+ }
app/routes/_index.tsx CHANGED
@@ -1,138 +1,179 @@
1
- import type { MetaFunction } from "@remix-run/node";
 
 
 
2
 
3
  export const meta: MetaFunction = () => {
4
  return [
5
- { title: "New Remix App" },
6
- { name: "description", content: "Welcome to Remix!" },
7
  ];
8
  };
9
 
 
 
 
 
 
 
 
 
10
  export default function Index() {
 
 
 
11
  return (
12
- <div className="flex h-screen items-center justify-center">
13
- <div className="flex flex-col items-center gap-16">
14
- <header className="flex flex-col items-center gap-9">
15
- <h1 className="leading text-2xl font-bold text-gray-800 dark:text-gray-100">
16
- Welcome to <span className="sr-only">Remix</span>
17
- </h1>
18
- <div className="h-[144px] w-[434px]">
19
- <img
20
- src="/logo-light.png"
21
- alt="Remix"
22
- className="block w-full dark:hidden"
23
- />
24
- <img
25
- src="/logo-dark.png"
26
- alt="Remix"
27
- className="hidden w-full dark:block"
28
- />
29
- </div>
30
- </header>
31
- <nav className="flex flex-col items-center justify-center gap-4 rounded-3xl border border-gray-200 p-6 dark:border-gray-700">
32
- <p className="leading-6 text-gray-700 dark:text-gray-200">
33
- What&apos;s next?
34
- </p>
35
- <ul>
36
- {resources.map(({ href, text, icon }) => (
37
- <li key={href}>
38
- <a
39
- className="group flex items-center gap-3 self-stretch p-3 leading-normal text-blue-700 hover:underline dark:text-blue-500"
40
- href={href}
41
- target="_blank"
42
- rel="noreferrer"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  >
44
- {icon}
45
- {text}
46
- </a>
47
- </li>
48
- ))}
49
- </ul>
50
- </nav>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  </div>
52
  </div>
53
  );
54
  }
55
 
56
- const resources = [
57
- {
58
- href: "https://remix.run/start/quickstart",
59
- text: "Quick Start (5 min)",
60
- icon: (
61
- <svg
62
- xmlns="http://www.w3.org/2000/svg"
63
- width="24"
64
- height="20"
65
- viewBox="0 0 20 20"
66
- fill="none"
67
- className="stroke-gray-600 group-hover:stroke-current dark:stroke-gray-300"
68
- >
69
- <path
70
- d="M8.51851 12.0741L7.92592 18L15.6296 9.7037L11.4815 7.33333L12.0741 2L4.37036 10.2963L8.51851 12.0741Z"
71
- strokeWidth="1.5"
72
- strokeLinecap="round"
73
- strokeLinejoin="round"
74
- />
75
- </svg>
76
- ),
77
- },
78
- {
79
- href: "https://remix.run/start/tutorial",
80
- text: "Tutorial (30 min)",
81
- icon: (
82
- <svg
83
- xmlns="http://www.w3.org/2000/svg"
84
- width="24"
85
- height="20"
86
- viewBox="0 0 20 20"
87
- fill="none"
88
- className="stroke-gray-600 group-hover:stroke-current dark:stroke-gray-300"
89
- >
90
- <path
91
- d="M4.561 12.749L3.15503 14.1549M3.00811 8.99944H1.01978M3.15503 3.84489L4.561 5.2508M8.3107 1.70923L8.3107 3.69749M13.4655 3.84489L12.0595 5.2508M18.1868 17.0974L16.635 18.6491C16.4636 18.8205 16.1858 18.8205 16.0144 18.6491L13.568 16.2028C13.383 16.0178 13.0784 16.0347 12.915 16.239L11.2697 18.2956C11.047 18.5739 10.6029 18.4847 10.505 18.142L7.85215 8.85711C7.75756 8.52603 8.06365 8.21994 8.39472 8.31453L17.6796 10.9673C18.0223 11.0653 18.1115 11.5094 17.8332 11.7321L15.7766 13.3773C15.5723 13.5408 15.5554 13.8454 15.7404 14.0304L18.1868 16.4767C18.3582 16.6481 18.3582 16.926 18.1868 17.0974Z"
92
- strokeWidth="1.5"
93
- strokeLinecap="round"
94
- strokeLinejoin="round"
95
- />
96
- </svg>
97
- ),
98
- },
99
- {
100
- href: "https://remix.run/docs",
101
- text: "Remix Docs",
102
- icon: (
103
- <svg
104
- xmlns="http://www.w3.org/2000/svg"
105
- width="24"
106
- height="20"
107
- viewBox="0 0 20 20"
108
- fill="none"
109
- className="stroke-gray-600 group-hover:stroke-current dark:stroke-gray-300"
110
- >
111
- <path
112
- d="M9.99981 10.0751V9.99992M17.4688 17.4688C15.889 19.0485 11.2645 16.9853 7.13958 12.8604C3.01467 8.73546 0.951405 4.11091 2.53116 2.53116C4.11091 0.951405 8.73546 3.01467 12.8604 7.13958C16.9853 11.2645 19.0485 15.889 17.4688 17.4688ZM2.53132 17.4688C0.951566 15.8891 3.01483 11.2645 7.13974 7.13963C11.2647 3.01471 15.8892 0.951453 17.469 2.53121C19.0487 4.11096 16.9854 8.73551 12.8605 12.8604C8.73562 16.9853 4.11107 19.0486 2.53132 17.4688Z"
113
- strokeWidth="1.5"
114
- strokeLinecap="round"
115
- />
116
- </svg>
117
- ),
118
- },
119
- {
120
- href: "https://rmx.as/discord",
121
- text: "Join Discord",
122
- icon: (
123
- <svg
124
- xmlns="http://www.w3.org/2000/svg"
125
- width="24"
126
- height="20"
127
- viewBox="0 0 24 20"
128
- fill="none"
129
- className="stroke-gray-600 group-hover:stroke-current dark:stroke-gray-300"
130
- >
131
- <path
132
- d="M15.0686 1.25995L14.5477 1.17423L14.2913 1.63578C14.1754 1.84439 14.0545 2.08275 13.9422 2.31963C12.6461 2.16488 11.3406 2.16505 10.0445 2.32014C9.92822 2.08178 9.80478 1.84975 9.67412 1.62413L9.41449 1.17584L8.90333 1.25995C7.33547 1.51794 5.80717 1.99419 4.37748 2.66939L4.19 2.75793L4.07461 2.93019C1.23864 7.16437 0.46302 11.3053 0.838165 15.3924L0.868838 15.7266L1.13844 15.9264C2.81818 17.1714 4.68053 18.1233 6.68582 18.719L7.18892 18.8684L7.50166 18.4469C7.96179 17.8268 8.36504 17.1824 8.709 16.4944L8.71099 16.4904C10.8645 17.0471 13.128 17.0485 15.2821 16.4947C15.6261 17.1826 16.0293 17.8269 16.4892 18.4469L16.805 18.8725L17.3116 18.717C19.3056 18.105 21.1876 17.1751 22.8559 15.9238L23.1224 15.724L23.1528 15.3923C23.5873 10.6524 22.3579 6.53306 19.8947 2.90714L19.7759 2.73227L19.5833 2.64518C18.1437 1.99439 16.6386 1.51826 15.0686 1.25995ZM16.6074 10.7755L16.6074 10.7756C16.5934 11.6409 16.0212 12.1444 15.4783 12.1444C14.9297 12.1444 14.3493 11.6173 14.3493 10.7877C14.3493 9.94885 14.9378 9.41192 15.4783 9.41192C16.0471 9.41192 16.6209 9.93851 16.6074 10.7755ZM8.49373 12.1444C7.94513 12.1444 7.36471 11.6173 7.36471 10.7877C7.36471 9.94885 7.95323 9.41192 8.49373 9.41192C9.06038 9.41192 9.63892 9.93712 9.6417 10.7815C9.62517 11.6239 9.05462 12.1444 8.49373 12.1444Z"
133
- strokeWidth="1.5"
134
- />
135
- </svg>
136
- ),
137
- },
138
- ];
 
1
+ import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/node";
2
+ import { json } from "@remix-run/node";
3
+ import { useLoaderData, Link, useSearchParams } from "@remix-run/react";
4
+ import { getUserSession } from "~/lib/session.server";
5
 
6
  export const meta: MetaFunction = () => {
7
  return [
8
+ { title: "HugeX GitHub App" },
9
+ { name: "description", content: "GitHub App with user authentication" },
10
  ];
11
  };
12
 
13
+ export async function loader({ request }: LoaderFunctionArgs) {
14
+ const user = await getUserSession(request);
15
+ const url = new URL(request.url);
16
+ const error = url.searchParams.get("error");
17
+
18
+ return json({ user, error });
19
+ }
20
+
21
  export default function Index() {
22
+ const { user, error } = useLoaderData<typeof loader>();
23
+ const [searchParams] = useSearchParams();
24
+
25
  return (
26
+ <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
27
+ <div className="flex h-screen items-center justify-center">
28
+ <div className="flex flex-col items-center gap-8 max-w-2xl mx-auto px-4">
29
+ <header className="flex flex-col items-center gap-6">
30
+ <div className="bg-white rounded-full p-4 shadow-lg">
31
+ <svg
32
+ className="w-16 h-16 text-blue-600"
33
+ fill="currentColor"
34
+ viewBox="0 0 24 24"
35
+ >
36
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
37
+ </svg>
38
+ </div>
39
+ <h1 className="text-4xl font-bold text-gray-900">
40
+ HugeX GitHub App
41
+ </h1>
42
+ <p className="text-lg text-gray-600 text-center">
43
+ GitHub App with user authentication and webhook support
44
+ </p>
45
+ </header>
46
+
47
+ {/* Error Messages */}
48
+ {error && (
49
+ <div className="bg-red-50 border border-red-200 rounded-lg p-4 w-full">
50
+ <div className="flex">
51
+ <div className="flex-shrink-0">
52
+ <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
53
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
54
+ </svg>
55
+ </div>
56
+ <div className="ml-3">
57
+ <h3 className="text-sm font-medium text-red-800">
58
+ Authentication Error
59
+ </h3>
60
+ <div className="mt-2 text-sm text-red-700">
61
+ {error === "oauth_failed" && "OAuth authentication failed. Please try again."}
62
+ {error === "no_code" && "No authorization code received from GitHub."}
63
+ {error === "callback_failed" && "Failed to complete authentication. Please try again."}
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ )}
69
+
70
+ {/* Main Content */}
71
+ {user ? (
72
+ <div className="bg-white rounded-lg shadow-lg p-8 w-full">
73
+ <div className="text-center">
74
+ <div className="flex items-center justify-center mb-4">
75
+ {user.avatar_url && (
76
+ <img
77
+ src={user.avatar_url}
78
+ alt={user.login}
79
+ className="w-16 h-16 rounded-full mr-4"
80
+ />
81
+ )}
82
+ <div>
83
+ <h2 className="text-2xl font-bold text-gray-900">
84
+ Welcome back, {user.name || user.login}!
85
+ </h2>
86
+ <p className="text-gray-600">@{user.login}</p>
87
+ </div>
88
+ </div>
89
+ <div className="mt-6 flex gap-4 justify-center">
90
+ <Link
91
+ to="/dashboard"
92
+ className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
93
+ >
94
+ Go to Dashboard
95
+ </Link>
96
+ <Link
97
+ to="/status"
98
+ className="inline-flex items-center px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
99
+ >
100
+ Check Status
101
+ </Link>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ ) : (
106
+ <div className="bg-white rounded-lg shadow-lg p-8 w-full">
107
+ <div className="text-center">
108
+ <h2 className="text-2xl font-bold text-gray-900 mb-4">
109
+ Get Started
110
+ </h2>
111
+ <p className="text-gray-600 mb-6">
112
+ Authenticate with GitHub to enable user-specific actions when webhooks are triggered.
113
+ </p>
114
+ <Link
115
+ to="/auth/github"
116
+ className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
117
  >
118
+ <svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
119
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
120
+ </svg>
121
+ Sign in with GitHub
122
+ </Link>
123
+ <div className="mt-4">
124
+ <Link
125
+ to="/status"
126
+ className="text-sm text-blue-600 hover:text-blue-700 font-medium"
127
+ >
128
+ Check Environment Status
129
+ </Link>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ )}
134
+
135
+ {/* Features */}
136
+ <div className="grid md:grid-cols-3 gap-6 w-full mt-8">
137
+ <div className="bg-white rounded-lg shadow p-6">
138
+ <div className="text-blue-600 mb-3">
139
+ <svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
140
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
141
+ </svg>
142
+ </div>
143
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">User Authentication</h3>
144
+ <p className="text-gray-600 text-sm">
145
+ Authenticate users with GitHub OAuth to perform actions on their behalf.
146
+ </p>
147
+ </div>
148
+
149
+ <div className="bg-white rounded-lg shadow p-6">
150
+ <div className="text-green-600 mb-3">
151
+ <svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
152
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
153
+ </svg>
154
+ </div>
155
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">Webhook Support</h3>
156
+ <p className="text-gray-600 text-sm">
157
+ Handle GitHub webhooks and associate them with authenticated users.
158
+ </p>
159
+ </div>
160
+
161
+ <div className="bg-white rounded-lg shadow p-6">
162
+ <div className="text-purple-600 mb-3">
163
+ <svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
164
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
165
+ </svg>
166
+ </div>
167
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">API Integration</h3>
168
+ <p className="text-gray-600 text-sm">
169
+ Use authenticated user sessions to make GitHub API calls.
170
+ </p>
171
+ </div>
172
+ </div>
173
+ </div>
174
  </div>
175
  </div>
176
  );
177
  }
178
 
179
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/routes/auth.github.callback.tsx ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { redirect } from "@remix-run/node";
2
+ import type { LoaderFunctionArgs } from "@remix-run/node";
3
+ import { githubApp } from "~/lib/github-app.server";
4
+ import { getSession, commitSession } from "~/lib/session.server";
5
+
6
+ export async function loader({ request }: LoaderFunctionArgs) {
7
+ const url = new URL(request.url);
8
+ const code = url.searchParams.get("code");
9
+ const state = url.searchParams.get("state");
10
+ const error = url.searchParams.get("error");
11
+
12
+ // Handle OAuth errors
13
+ if (error) {
14
+ console.error("GitHub OAuth error:", error);
15
+ return redirect("/?error=oauth_failed");
16
+ }
17
+
18
+ if (!code) {
19
+ console.error("No authorization code received");
20
+ return redirect("/?error=no_code");
21
+ }
22
+
23
+ try {
24
+ // Exchange code for access token and get user info
25
+ const userAuth = await githubApp.handleCallback(code, state || undefined);
26
+
27
+ // Create user session
28
+ const session = await getSession(request.headers.get("Cookie"));
29
+ session.set("user", {
30
+ userId: userAuth.id.toString(),
31
+ login: userAuth.login,
32
+ name: userAuth.name,
33
+ email: userAuth.email,
34
+ avatar_url: userAuth.avatar_url,
35
+ });
36
+
37
+ return redirect("/dashboard", {
38
+ headers: {
39
+ "Set-Cookie": await commitSession(session),
40
+ },
41
+ });
42
+ } catch (error) {
43
+ console.error("GitHub callback error:", error);
44
+ return redirect("/?error=callback_failed");
45
+ }
46
+ }
app/routes/auth.github.tsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { redirect } from "@remix-run/node";
2
+ import type { LoaderFunctionArgs } from "@remix-run/node";
3
+ import { githubApp } from "~/lib/github-app.server";
4
+
5
+ export async function loader({ request }: LoaderFunctionArgs) {
6
+ const url = new URL(request.url);
7
+ const state = url.searchParams.get("state") || crypto.randomUUID();
8
+
9
+ // Generate OAuth URL for user identity authorization
10
+ const oauthUrl = githubApp.getOAuthUrl(state);
11
+
12
+ return redirect(oauthUrl);
13
+ }
app/routes/auth.logout.tsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { redirect } from "@remix-run/node";
2
+ import type { ActionFunctionArgs } from "@remix-run/node";
3
+ import { getSession, destroySession } from "~/lib/session.server";
4
+
5
+ export async function action({ request }: ActionFunctionArgs) {
6
+ const session = await getSession(request.headers.get("Cookie"));
7
+
8
+ return redirect("/", {
9
+ headers: {
10
+ "Set-Cookie": await destroySession(session),
11
+ },
12
+ });
13
+ }
14
+
15
+ export async function loader() {
16
+ return redirect("/");
17
+ }
app/routes/dashboard.tsx ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { json } from "@remix-run/node";
2
+ import type { LoaderFunctionArgs } from "@remix-run/node";
3
+ import { useLoaderData, Form } from "@remix-run/react";
4
+ import { requireUserSession } from "~/lib/session.server";
5
+ import { githubApp } from "~/lib/github-app.server";
6
+
7
+ export async function loader({ request }: LoaderFunctionArgs) {
8
+ const userSession = await requireUserSession(request);
9
+
10
+ // Get additional user auth info from our store
11
+ const userAuth = githubApp.getUserAuth(userSession.login);
12
+
13
+ return json({
14
+ user: userSession,
15
+ userAuth,
16
+ allAuths: githubApp.getAllUserAuths(), // For debugging
17
+ });
18
+ }
19
+
20
+ export default function Dashboard() {
21
+ const { user, userAuth, allAuths } = useLoaderData<typeof loader>();
22
+
23
+ return (
24
+ <div className="min-h-screen bg-gray-50 py-8">
25
+ <div className="max-w-4xl mx-auto px-4">
26
+ <div className="bg-white rounded-lg shadow-md p-6">
27
+ <div className="flex items-center justify-between mb-6">
28
+ <h1 className="text-2xl font-bold text-gray-900">
29
+ GitHub App Dashboard
30
+ </h1>
31
+ <Form method="post" action="/auth/logout">
32
+ <button
33
+ type="submit"
34
+ className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md text-sm font-medium"
35
+ >
36
+ Logout
37
+ </button>
38
+ </Form>
39
+ </div>
40
+
41
+ <div className="grid md:grid-cols-2 gap-6">
42
+ {/* User Info */}
43
+ <div className="bg-gray-50 rounded-lg p-4">
44
+ <h2 className="text-lg font-semibold text-gray-900 mb-4">
45
+ User Information
46
+ </h2>
47
+ <div className="flex items-center mb-4">
48
+ {user.avatar_url && (
49
+ <img
50
+ src={user.avatar_url}
51
+ alt={user.login}
52
+ className="w-12 h-12 rounded-full mr-4"
53
+ />
54
+ )}
55
+ <div>
56
+ <p className="font-medium text-gray-900">{user.name || user.login}</p>
57
+ <p className="text-gray-600">@{user.login}</p>
58
+ {user.email && (
59
+ <p className="text-gray-600 text-sm">{user.email}</p>
60
+ )}
61
+ </div>
62
+ </div>
63
+ </div>
64
+
65
+ {/* Auth Info */}
66
+ <div className="bg-gray-50 rounded-lg p-4">
67
+ <h2 className="text-lg font-semibold text-gray-900 mb-4">
68
+ Authentication Status
69
+ </h2>
70
+ <div className="space-y-2">
71
+ <p className="text-sm">
72
+ <span className="font-medium">Status:</span>{' '}
73
+ <span className="text-green-600">✅ Authenticated</span>
74
+ </p>
75
+ <p className="text-sm">
76
+ <span className="font-medium">User ID:</span> {user.userId}
77
+ </p>
78
+ {userAuth && (
79
+ <>
80
+ <p className="text-sm">
81
+ <span className="font-medium">Authenticated At:</span>{' '}
82
+ {new Date(userAuth.authenticated_at).toLocaleString()}
83
+ </p>
84
+ {userAuth.state && (
85
+ <p className="text-sm">
86
+ <span className="font-medium">State:</span> {userAuth.state}
87
+ </p>
88
+ )}
89
+ </>
90
+ )}
91
+ </div>
92
+ </div>
93
+ </div>
94
+
95
+ {/* Debug Info */}
96
+ <div className="mt-8 bg-yellow-50 border border-yellow-200 rounded-lg p-4">
97
+ <h3 className="text-lg font-semibold text-yellow-800 mb-2">
98
+ Debug Information
99
+ </h3>
100
+ <p className="text-sm text-yellow-700 mb-2">
101
+ All authenticated users ({allAuths.length}):
102
+ </p>
103
+ <div className="bg-white rounded border p-3 max-h-48 overflow-y-auto">
104
+ <pre className="text-xs text-gray-600">
105
+ {JSON.stringify(allAuths, null, 2)}
106
+ </pre>
107
+ </div>
108
+ </div>
109
+
110
+ {/* Usage Instructions */}
111
+ <div className="mt-8 bg-blue-50 border border-blue-200 rounded-lg p-4">
112
+ <h3 className="text-lg font-semibold text-blue-800 mb-2">
113
+ Next Steps
114
+ </h3>
115
+ <div className="text-sm text-blue-700 space-y-2">
116
+ <p>✅ User authentication is working!</p>
117
+ <p>
118
+ 🔧 Now you can use the authenticated user identity when GitHub
119
+ triggers your app webhooks
120
+ </p>
121
+ <p>
122
+ 📝 The user's authentication info is stored and can be retrieved
123
+ using: <code className="bg-blue-100 px-1 rounded">githubApp.getUserAuth('{user.login}')</code>
124
+ </p>
125
+ <p>
126
+ 🔗 You can create an authenticated Octokit instance using:{' '}
127
+ <code className="bg-blue-100 px-1 rounded">githubApp.getUserOctokit('{user.login}')</code>
128
+ </p>
129
+ <div className="mt-4">
130
+ <Link
131
+ to="/install"
132
+ className="inline-flex items-center px-4 py-2 border border-blue-300 text-sm font-medium rounded-md text-blue-700 bg-white hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
133
+ >
134
+ 📦 Install GitHub App on Repositories
135
+ </Link>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ );
143
+ }
app/routes/install.tsx ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { json } from "@remix-run/node";
2
+ import type { LoaderFunctionArgs } from "@remix-run/node";
3
+ import { useLoaderData, Link } from "@remix-run/react";
4
+ import { requireUserSession } from "~/lib/session.server";
5
+ import { githubApp } from "~/lib/github-app.server";
6
+
7
+ export async function loader({ request }: LoaderFunctionArgs) {
8
+ const userSession = await requireUserSession(request);
9
+
10
+ // Generate installation URL
11
+ const installationUrl = githubApp.getInstallationUrl(
12
+ `user:${userSession.login}`
13
+ );
14
+
15
+ return json({
16
+ user: userSession,
17
+ installationUrl,
18
+ });
19
+ }
20
+
21
+ export default function InstallApp() {
22
+ const { user, installationUrl } = useLoaderData<typeof loader>();
23
+
24
+ return (
25
+ <div className="min-h-screen bg-gray-50 py-8">
26
+ <div className="max-w-2xl mx-auto px-4">
27
+ <div className="bg-white rounded-lg shadow-md p-6">
28
+ <div className="flex items-center justify-between mb-6">
29
+ <h1 className="text-2xl font-bold text-gray-900">
30
+ Install GitHub App
31
+ </h1>
32
+ <Link
33
+ to="/dashboard"
34
+ className="text-blue-600 hover:text-blue-700 text-sm font-medium"
35
+ >
36
+ ← Back to Dashboard
37
+ </Link>
38
+ </div>
39
+
40
+ <div className="space-y-6">
41
+ <div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
42
+ <div className="flex">
43
+ <div className="flex-shrink-0">
44
+ <svg className="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor">
45
+ <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
46
+ </svg>
47
+ </div>
48
+ <div className="ml-3">
49
+ <h3 className="text-sm font-medium text-blue-800">
50
+ Install Required
51
+ </h3>
52
+ <div className="mt-2 text-sm text-blue-700">
53
+ <p>
54
+ To receive webhooks and interact with repositories, you need to install
55
+ the HugeX GitHub App on your repositories or organization.
56
+ </p>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+
62
+ <div className="bg-gray-50 rounded-lg p-4">
63
+ <h2 className="text-lg font-semibold text-gray-900 mb-4">
64
+ What happens when you install?
65
+ </h2>
66
+ <ul className="space-y-2 text-gray-600">
67
+ <li className="flex items-start">
68
+ <svg className="w-5 h-5 text-green-500 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
69
+ <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
70
+ </svg>
71
+ The app will be able to receive webhook events from your selected repositories
72
+ </li>
73
+ <li className="flex items-start">
74
+ <svg className="w-5 h-5 text-green-500 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
75
+ <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
76
+ </svg>
77
+ Events will be associated with your authenticated user account
78
+ </li>
79
+ <li className="flex items-start">
80
+ <svg className="w-5 h-5 text-green-500 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
81
+ <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
82
+ </svg>
83
+ The app can perform actions on your behalf using your user token
84
+ </li>
85
+ <li className="flex items-start">
86
+ <svg className="w-5 h-5 text-green-500 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
87
+ <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
88
+ </svg>
89
+ You can uninstall the app at any time from your GitHub settings
90
+ </li>
91
+ </ul>
92
+ </div>
93
+
94
+ <div className="text-center">
95
+ <a
96
+ href={installationUrl}
97
+ target="_blank"
98
+ rel="noopener noreferrer"
99
+ className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
100
+ >
101
+ <svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
102
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
103
+ </svg>
104
+ Install HugeX GitHub App
105
+ </a>
106
+ </div>
107
+
108
+ <div className="text-center text-sm text-gray-500">
109
+ <p>
110
+ This will open GitHub in a new tab where you can choose which repositories
111
+ to install the app on. You can install it on specific repositories or your entire organization.
112
+ </p>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ );
119
+ }
app/routes/status.tsx ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { json } from "@remix-run/node";
2
+ import type { LoaderFunctionArgs } from "@remix-run/node";
3
+ import { useLoaderData, Link } from "@remix-run/react";
4
+
5
+ export async function loader({ request }: LoaderFunctionArgs) {
6
+ // Check environment variables
7
+ const envCheck = {
8
+ GITHUB_APP_ID: {
9
+ value: process.env.GITHUB_APP_ID || null,
10
+ required: true,
11
+ example: "1356087",
12
+ },
13
+ GITHUB_APP_NAME: {
14
+ value: process.env.GITHUB_APP_NAME || null,
15
+ required: true,
16
+ example: "hugex-gh",
17
+ },
18
+ GITHUB_APP_PRIVATE_KEY: {
19
+ value: process.env.GITHUB_APP_PRIVATE_KEY ? "✓ Set" : null,
20
+ required: true,
21
+ example: "-----BEGIN RSA PRIVATE KEY-----...",
22
+ },
23
+ GITHUB_APP_CLIENT_ID: {
24
+ value: process.env.GITHUB_APP_CLIENT_ID || null,
25
+ required: true,
26
+ example: "Iv23liFxEtiiREnjOeB2",
27
+ },
28
+ GITHUB_APP_CLIENT_SECRET: {
29
+ value: process.env.GITHUB_APP_CLIENT_SECRET ? "✓ Set" : null,
30
+ required: true,
31
+ example: "your-client-secret",
32
+ },
33
+ GITHUB_WEBHOOK_SECRET: {
34
+ value: process.env.GITHUB_WEBHOOK_SECRET ? "✓ Set" : null,
35
+ required: false,
36
+ example: "your-webhook-secret",
37
+ },
38
+ GITHUB_CALLBACK_URL: {
39
+ value: process.env.GITHUB_CALLBACK_URL || null,
40
+ required: true,
41
+ example: "http://localhost:3000/auth/github/callback",
42
+ },
43
+ SESSION_SECRET: {
44
+ value: process.env.SESSION_SECRET ? "✓ Set" : null,
45
+ required: true,
46
+ example: "your-session-secret",
47
+ },
48
+ };
49
+
50
+ const missing = Object.entries(envCheck).filter(
51
+ ([key, config]) => config.required && !config.value
52
+ );
53
+
54
+ const isReady = missing.length === 0;
55
+
56
+ return json({
57
+ envCheck,
58
+ missing,
59
+ isReady,
60
+ nodeEnv: process.env.NODE_ENV || "development",
61
+ });
62
+ }
63
+
64
+ export default function Status() {
65
+ const { envCheck, missing, isReady, nodeEnv } = useLoaderData<typeof loader>();
66
+
67
+ return (
68
+ <div className="min-h-screen bg-gray-50 py-8">
69
+ <div className="max-w-4xl mx-auto px-4">
70
+ <div className="bg-white rounded-lg shadow-md p-6">
71
+ <div className="flex items-center justify-between mb-6">
72
+ <h1 className="text-2xl font-bold text-gray-900">
73
+ Environment Status
74
+ </h1>
75
+ <Link
76
+ to="/"
77
+ className="text-blue-600 hover:text-blue-700 text-sm font-medium"
78
+ >
79
+ ← Back to Home
80
+ </Link>
81
+ </div>
82
+
83
+ {/* Status Overview */}
84
+ <div className="mb-8">
85
+ <div className={`p-4 rounded-lg border ${
86
+ isReady
87
+ ? 'bg-green-50 border-green-200'
88
+ : 'bg-red-50 border-red-200'
89
+ }`}>
90
+ <div className="flex items-center">
91
+ <div className="flex-shrink-0">
92
+ {isReady ? (
93
+ <svg className="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
94
+ <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
95
+ </svg>
96
+ ) : (
97
+ <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
98
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
99
+ </svg>
100
+ )}
101
+ </div>
102
+ <div className="ml-3">
103
+ <h3 className={`text-sm font-medium ${
104
+ isReady ? 'text-green-800' : 'text-red-800'
105
+ }`}>
106
+ {isReady ? '✅ Ready to go!' : '⚠️ Configuration needed'}
107
+ </h3>
108
+ <div className={`mt-2 text-sm ${
109
+ isReady ? 'text-green-700' : 'text-red-700'
110
+ }`}>
111
+ {isReady ? (
112
+ <p>All required environment variables are configured.</p>
113
+ ) : (
114
+ <p>
115
+ {missing.length} required environment variable{missing.length > 1 ? 's' : ''} missing.
116
+ </p>
117
+ )}
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+
124
+ {/* Environment Variables Table */}
125
+ <div className="mb-8">
126
+ <h2 className="text-lg font-semibold text-gray-900 mb-4">
127
+ Environment Variables
128
+ </h2>
129
+ <div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
130
+ <table className="min-w-full divide-y divide-gray-300">
131
+ <thead className="bg-gray-50">
132
+ <tr>
133
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
134
+ Variable
135
+ </th>
136
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
137
+ Status
138
+ </th>
139
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
140
+ Required
141
+ </th>
142
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
143
+ Example
144
+ </th>
145
+ </tr>
146
+ </thead>
147
+ <tbody className="bg-white divide-y divide-gray-200">
148
+ {Object.entries(envCheck).map(([key, config]) => (
149
+ <tr key={key}>
150
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
151
+ {key}
152
+ </td>
153
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
154
+ {config.value ? (
155
+ <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
156
+ {config.value}
157
+ </span>
158
+ ) : (
159
+ <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
160
+ Not set
161
+ </span>
162
+ )}
163
+ </td>
164
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
165
+ {config.required ? '✅ Yes' : '⚪ Optional'}
166
+ </td>
167
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono">
168
+ {config.example}
169
+ </td>
170
+ </tr>
171
+ ))}
172
+ </tbody>
173
+ </table>
174
+ </div>
175
+ </div>
176
+
177
+ {/* Missing Variables */}
178
+ {missing.length > 0 && (
179
+ <div className="mb-8">
180
+ <h2 className="text-lg font-semibold text-gray-900 mb-4">
181
+ Missing Configuration
182
+ </h2>
183
+ <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
184
+ <div className="flex">
185
+ <div className="flex-shrink-0">
186
+ <svg className="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
187
+ <path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
188
+ </svg>
189
+ </div>
190
+ <div className="ml-3">
191
+ <h3 className="text-sm font-medium text-yellow-800">
192
+ Add these to your .env file:
193
+ </h3>
194
+ <div className="mt-2 text-sm text-yellow-700">
195
+ <div className="bg-gray-900 text-green-400 p-3 rounded font-mono text-xs overflow-x-auto">
196
+ {missing.map(([key, config]) => (
197
+ <div key={key}>
198
+ {key}={config.example}
199
+ </div>
200
+ ))}
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ )}
208
+
209
+ {/* Quick Actions */}
210
+ <div className="bg-gray-50 rounded-lg p-4">
211
+ <h3 className="text-lg font-semibold text-gray-900 mb-4">
212
+ Quick Actions
213
+ </h3>
214
+ <div className="grid md:grid-cols-2 gap-4">
215
+ <div>
216
+ <h4 className="font-medium text-gray-900 mb-2">Generate Secrets</h4>
217
+ <p className="text-sm text-gray-600 mb-3">
218
+ Generate random secrets for SESSION_SECRET and GITHUB_WEBHOOK_SECRET:
219
+ </p>
220
+ <code className="text-xs bg-gray-800 text-green-400 p-2 rounded block">
221
+ npm run secrets
222
+ </code>
223
+ </div>
224
+ <div>
225
+ <h4 className="font-medium text-gray-900 mb-2">Environment</h4>
226
+ <p className="text-sm text-gray-600 mb-2">
227
+ Current environment: <span className="font-mono">{nodeEnv}</span>
228
+ </p>
229
+ <p className="text-sm text-gray-600">
230
+ {nodeEnv === 'development' ? (
231
+ <>✅ Development mode - using .env file</>
232
+ ) : (
233
+ <>🚀 Production mode - using system environment</>
234
+ )}
235
+ </p>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ );
243
+ }
app/routes/webhook.github.tsx ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { json } from "@remix-run/node";
2
+ import type { ActionFunctionArgs } from "@remix-run/node";
3
+ import { githubApp } from "~/lib/github-app.server";
4
+
5
+ export async function action({ request }: ActionFunctionArgs) {
6
+ if (request.method !== "POST") {
7
+ return json({ error: "Method not allowed" }, { status: 405 });
8
+ }
9
+
10
+ try {
11
+ const payload = await request.text();
12
+ const signature = request.headers.get("x-hub-signature-256") || "";
13
+ const event = request.headers.get("x-github-event") || "";
14
+ const delivery = request.headers.get("x-github-delivery") || "";
15
+
16
+ // Verify webhook signature
17
+ if (!githubApp.verifyWebhookSignature(payload, signature)) {
18
+ console.error("Invalid webhook signature");
19
+ return json({ error: "Invalid signature" }, { status: 401 });
20
+ }
21
+
22
+ const eventData = JSON.parse(payload);
23
+
24
+ console.log(`📥 Received GitHub webhook: ${event} (${delivery})`);
25
+ console.log("Event data:", JSON.stringify(eventData, null, 2));
26
+
27
+ // Handle different webhook events
28
+ switch (event) {
29
+ case "installation":
30
+ await handleInstallationEvent(eventData);
31
+ break;
32
+
33
+ case "installation_repositories":
34
+ await handleInstallationRepositoriesEvent(eventData);
35
+ break;
36
+
37
+ case "push":
38
+ await handlePushEvent(eventData);
39
+ break;
40
+
41
+ case "pull_request":
42
+ await handlePullRequestEvent(eventData);
43
+ break;
44
+
45
+ case "issues":
46
+ await handleIssuesEvent(eventData);
47
+ break;
48
+
49
+ default:
50
+ console.log(`Unhandled event type: ${event}`);
51
+ }
52
+
53
+ return json({ success: true, event, delivery });
54
+ } catch (error) {
55
+ console.error("Webhook error:", error);
56
+ return json({ error: "Internal server error" }, { status: 500 });
57
+ }
58
+ }
59
+
60
+ async function handleInstallationEvent(data: any) {
61
+ const { action, installation, repositories } = data;
62
+
63
+ console.log(`🔧 Installation ${action}:`, {
64
+ installationId: installation.id,
65
+ account: installation.account.login,
66
+ repositoryCount: repositories?.length || 0,
67
+ });
68
+
69
+ if (action === "created") {
70
+ // App was installed - you can store installation info here
71
+ console.log(`✅ App installed by ${installation.account.login}`);
72
+ } else if (action === "deleted") {
73
+ // App was uninstalled
74
+ console.log(`❌ App uninstalled by ${installation.account.login}`);
75
+ }
76
+ }
77
+
78
+ async function handleInstallationRepositoriesEvent(data: any) {
79
+ const { action, installation, repositories_added, repositories_removed } = data;
80
+
81
+ console.log(`📁 Installation repositories ${action}:`, {
82
+ installationId: installation.id,
83
+ added: repositories_added?.length || 0,
84
+ removed: repositories_removed?.length || 0,
85
+ });
86
+ }
87
+
88
+ async function handlePushEvent(data: any) {
89
+ const { repository, pusher, commits } = data;
90
+
91
+ console.log(`🚀 Push to ${repository.full_name}:`, {
92
+ pusher: pusher.name,
93
+ commits: commits.length,
94
+ branch: data.ref.replace('refs/heads/', ''),
95
+ });
96
+
97
+ // Example: Use the pusher's authenticated session
98
+ try {
99
+ const userAuth = githubApp.getUserAuth(pusher.name);
100
+ if (userAuth) {
101
+ console.log(`🔑 Found authenticated user: ${pusher.name}`);
102
+ // You can now use the user's authentication to perform actions on their behalf
103
+ // const userOctokit = await githubApp.getUserOctokit(pusher.name);
104
+ // Use userOctokit to make API calls as the authenticated user
105
+ } else {
106
+ console.log(`⚠️ No authentication found for user: ${pusher.name}`);
107
+ }
108
+ } catch (error) {
109
+ console.error("Error handling push event:", error);
110
+ }
111
+ }
112
+
113
+ async function handlePullRequestEvent(data: any) {
114
+ const { action, pull_request, repository } = data;
115
+
116
+ console.log(`🔀 Pull request ${action} in ${repository.full_name}:`, {
117
+ number: pull_request.number,
118
+ title: pull_request.title,
119
+ author: pull_request.user.login,
120
+ });
121
+
122
+ // Example: Use the PR author's authenticated session
123
+ try {
124
+ const userAuth = githubApp.getUserAuth(pull_request.user.login);
125
+ if (userAuth) {
126
+ console.log(`🔑 Found authenticated user: ${pull_request.user.login}`);
127
+ // You can now use the user's authentication to perform actions
128
+ } else {
129
+ console.log(`⚠️ No authentication found for user: ${pull_request.user.login}`);
130
+ }
131
+ } catch (error) {
132
+ console.error("Error handling pull request event:", error);
133
+ }
134
+ }
135
+
136
+ async function handleIssuesEvent(data: any) {
137
+ const { action, issue, repository } = data;
138
+
139
+ console.log(`🐛 Issue ${action} in ${repository.full_name}:`, {
140
+ number: issue.number,
141
+ title: issue.title,
142
+ author: issue.user.login,
143
+ });
144
+
145
+ // Example: Use the issue author's authenticated session
146
+ try {
147
+ const userAuth = githubApp.getUserAuth(issue.user.login);
148
+ if (userAuth) {
149
+ console.log(`🔑 Found authenticated user: ${issue.user.login}`);
150
+ // You can now use the user's authentication to perform actions
151
+ } else {
152
+ console.log(`⚠️ No authentication found for user: ${issue.user.login}`);
153
+ }
154
+ } catch (error) {
155
+ console.error("Error handling issues event:", error);
156
+ }
157
+ }
158
+
159
+ // Prevent GET requests
160
+ export async function loader() {
161
+ return json(
162
+ { error: "This endpoint only accepts POST requests from GitHub webhooks" },
163
+ { status: 405 }
164
+ );
165
+ }
package-lock.json CHANGED
@@ -6,15 +6,20 @@
6
  "": {
7
  "name": "hugex-gh",
8
  "dependencies": {
 
 
 
9
  "@remix-run/node": "^2.16.8",
10
  "@remix-run/react": "^2.16.8",
11
  "@remix-run/serve": "^2.16.8",
12
  "isbot": "^4.1.0",
 
13
  "react": "^18.2.0",
14
  "react-dom": "^18.2.0"
15
  },
16
  "devDependencies": {
17
  "@remix-run/dev": "^2.16.8",
 
18
  "@types/react": "^18.2.20",
19
  "@types/react-dom": "^18.2.7",
20
  "@typescript-eslint/eslint-plugin": "^6.7.4",
@@ -1446,6 +1451,454 @@
1446
  "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
1447
  }
1448
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1449
  "node_modules/@pkgjs/parseargs": {
1450
  "version": "0.11.0",
1451
  "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -2043,6 +2496,18 @@
2043
  "@types/estree": "*"
2044
  }
2045
  },
 
 
 
 
 
 
 
 
 
 
 
 
2046
  "node_modules/@types/cookie": {
2047
  "version": "0.6.0",
2048
  "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@@ -2100,6 +2565,16 @@
2100
  "dev": true,
2101
  "license": "MIT"
2102
  },
 
 
 
 
 
 
 
 
 
 
2103
  "node_modules/@types/mdast": {
2104
  "version": "3.0.15",
2105
  "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
@@ -2121,14 +2596,12 @@
2121
  "version": "2.1.0",
2122
  "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
2123
  "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
2124
- "dev": true,
2125
  "license": "MIT"
2126
  },
2127
  "node_modules/@types/node": {
2128
  "version": "22.15.29",
2129
  "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
2130
  "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
2131
- "dev": true,
2132
  "license": "MIT",
2133
  "dependencies": {
2134
  "undici-types": "~6.21.0"
@@ -3292,7 +3765,6 @@
3292
  "version": "3.1.0",
3293
  "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
3294
  "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
3295
- "dev": true,
3296
  "license": "MIT",
3297
  "dependencies": {
3298
  "clean-stack": "^2.0.0",
@@ -3722,6 +4194,12 @@
3722
  "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
3723
  "license": "MIT"
3724
  },
 
 
 
 
 
 
3725
  "node_modules/binary-extensions": {
3726
  "version": "2.3.0",
3727
  "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -3850,6 +4328,12 @@
3850
  "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
3851
  }
3852
  },
 
 
 
 
 
 
3853
  "node_modules/buffer": {
3854
  "version": "5.7.1",
3855
  "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -3875,6 +4359,12 @@
3875
  "ieee754": "^1.1.13"
3876
  }
3877
  },
 
 
 
 
 
 
3878
  "node_modules/buffer-from": {
3879
  "version": "1.1.2",
3880
  "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -4132,7 +4622,6 @@
4132
  "version": "2.2.0",
4133
  "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
4134
  "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
4135
- "dev": true,
4136
  "license": "MIT",
4137
  "engines": {
4138
  "node": ">=6"
@@ -4589,6 +5078,12 @@
4589
  "node": ">= 0.8"
4590
  }
4591
  },
 
 
 
 
 
 
4592
  "node_modules/dequal": {
4593
  "version": "2.0.3",
4594
  "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -4746,6 +5241,15 @@
4746
  "dev": true,
4747
  "license": "MIT"
4748
  },
 
 
 
 
 
 
 
 
 
4749
  "node_modules/ee-first": {
4750
  "version": "1.1.1",
4751
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -6733,7 +7237,6 @@
6733
  "version": "4.0.0",
6734
  "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
6735
  "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
6736
- "dev": true,
6737
  "license": "MIT",
6738
  "engines": {
6739
  "node": ">=8"
@@ -7541,6 +8044,28 @@
7541
  "graceful-fs": "^4.1.6"
7542
  }
7543
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7544
  "node_modules/jsx-ast-utils": {
7545
  "version": "3.3.5",
7546
  "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -7557,6 +8082,27 @@
7557
  "node": ">=4.0"
7558
  }
7559
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7560
  "node_modules/keyv": {
7561
  "version": "4.5.4",
7562
  "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -7696,6 +8242,42 @@
7696
  "dev": true,
7697
  "license": "MIT"
7698
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7699
  "node_modules/lodash.merge": {
7700
  "version": "4.6.2",
7701
  "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -7703,6 +8285,12 @@
7703
  "dev": true,
7704
  "license": "MIT"
7705
  },
 
 
 
 
 
 
7706
  "node_modules/log-symbols": {
7707
  "version": "4.1.0",
7708
  "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -9373,7 +9961,6 @@
9373
  "version": "1.4.0",
9374
  "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
9375
  "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
9376
- "dev": true,
9377
  "license": "ISC",
9378
  "dependencies": {
9379
  "wrappy": "1"
@@ -10825,7 +11412,6 @@
10825
  "version": "7.7.2",
10826
  "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
10827
  "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
10828
- "dev": true,
10829
  "license": "ISC",
10830
  "bin": {
10831
  "semver": "bin/semver.js"
@@ -12134,7 +12720,6 @@
12134
  "version": "6.21.0",
12135
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
12136
  "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
12137
- "dev": true,
12138
  "license": "MIT"
12139
  },
12140
  "node_modules/unified": {
@@ -12309,6 +12894,22 @@
12309
  "url": "https://opencollective.com/unified"
12310
  }
12311
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12312
  "node_modules/universalify": {
12313
  "version": "2.0.1",
12314
  "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -13345,7 +13946,6 @@
13345
  "version": "1.0.2",
13346
  "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
13347
  "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
13348
- "dev": true,
13349
  "license": "ISC"
13350
  },
13351
  "node_modules/ws": {
 
6
  "": {
7
  "name": "hugex-gh",
8
  "dependencies": {
9
+ "@octokit/app": "^14.0.2",
10
+ "@octokit/auth-app": "^6.0.1",
11
+ "@octokit/webhooks": "^12.0.4",
12
  "@remix-run/node": "^2.16.8",
13
  "@remix-run/react": "^2.16.8",
14
  "@remix-run/serve": "^2.16.8",
15
  "isbot": "^4.1.0",
16
+ "jsonwebtoken": "^9.0.2",
17
  "react": "^18.2.0",
18
  "react-dom": "^18.2.0"
19
  },
20
  "devDependencies": {
21
  "@remix-run/dev": "^2.16.8",
22
+ "@types/jsonwebtoken": "^9.0.5",
23
  "@types/react": "^18.2.20",
24
  "@types/react-dom": "^18.2.7",
25
  "@typescript-eslint/eslint-plugin": "^6.7.4",
 
1451
  "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
1452
  }
1453
  },
1454
+ "node_modules/@octokit/app": {
1455
+ "version": "14.1.0",
1456
+ "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz",
1457
+ "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==",
1458
+ "license": "MIT",
1459
+ "dependencies": {
1460
+ "@octokit/auth-app": "^6.0.0",
1461
+ "@octokit/auth-unauthenticated": "^5.0.0",
1462
+ "@octokit/core": "^5.0.0",
1463
+ "@octokit/oauth-app": "^6.0.0",
1464
+ "@octokit/plugin-paginate-rest": "^9.0.0",
1465
+ "@octokit/types": "^12.0.0",
1466
+ "@octokit/webhooks": "^12.0.4"
1467
+ },
1468
+ "engines": {
1469
+ "node": ">= 18"
1470
+ }
1471
+ },
1472
+ "node_modules/@octokit/auth-app": {
1473
+ "version": "6.1.3",
1474
+ "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.3.tgz",
1475
+ "integrity": "sha512-dcaiteA6Y/beAlDLZOPNReN3FGHu+pARD6OHfh3T9f3EO09++ec+5wt3KtGGSSs2Mp5tI8fQwdMOEnrzBLfgUA==",
1476
+ "license": "MIT",
1477
+ "dependencies": {
1478
+ "@octokit/auth-oauth-app": "^7.1.0",
1479
+ "@octokit/auth-oauth-user": "^4.1.0",
1480
+ "@octokit/request": "^8.3.1",
1481
+ "@octokit/request-error": "^5.1.0",
1482
+ "@octokit/types": "^13.1.0",
1483
+ "deprecation": "^2.3.1",
1484
+ "lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1",
1485
+ "universal-github-app-jwt": "^1.1.2",
1486
+ "universal-user-agent": "^6.0.0"
1487
+ },
1488
+ "engines": {
1489
+ "node": ">= 18"
1490
+ }
1491
+ },
1492
+ "node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": {
1493
+ "version": "24.2.0",
1494
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1495
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1496
+ "license": "MIT"
1497
+ },
1498
+ "node_modules/@octokit/auth-app/node_modules/@octokit/types": {
1499
+ "version": "13.10.0",
1500
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1501
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1502
+ "license": "MIT",
1503
+ "dependencies": {
1504
+ "@octokit/openapi-types": "^24.2.0"
1505
+ }
1506
+ },
1507
+ "node_modules/@octokit/auth-app/node_modules/lru-cache": {
1508
+ "name": "@wolfy1339/lru-cache",
1509
+ "version": "11.0.2-patch.1",
1510
+ "resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz",
1511
+ "integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==",
1512
+ "license": "ISC",
1513
+ "engines": {
1514
+ "node": "18 >=18.20 || 20 || >=22"
1515
+ }
1516
+ },
1517
+ "node_modules/@octokit/auth-oauth-app": {
1518
+ "version": "7.1.0",
1519
+ "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz",
1520
+ "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==",
1521
+ "license": "MIT",
1522
+ "dependencies": {
1523
+ "@octokit/auth-oauth-device": "^6.1.0",
1524
+ "@octokit/auth-oauth-user": "^4.1.0",
1525
+ "@octokit/request": "^8.3.1",
1526
+ "@octokit/types": "^13.0.0",
1527
+ "@types/btoa-lite": "^1.0.0",
1528
+ "btoa-lite": "^1.0.0",
1529
+ "universal-user-agent": "^6.0.0"
1530
+ },
1531
+ "engines": {
1532
+ "node": ">= 18"
1533
+ }
1534
+ },
1535
+ "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": {
1536
+ "version": "24.2.0",
1537
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1538
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1539
+ "license": "MIT"
1540
+ },
1541
+ "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": {
1542
+ "version": "13.10.0",
1543
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1544
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1545
+ "license": "MIT",
1546
+ "dependencies": {
1547
+ "@octokit/openapi-types": "^24.2.0"
1548
+ }
1549
+ },
1550
+ "node_modules/@octokit/auth-oauth-device": {
1551
+ "version": "6.1.0",
1552
+ "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz",
1553
+ "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==",
1554
+ "license": "MIT",
1555
+ "dependencies": {
1556
+ "@octokit/oauth-methods": "^4.1.0",
1557
+ "@octokit/request": "^8.3.1",
1558
+ "@octokit/types": "^13.0.0",
1559
+ "universal-user-agent": "^6.0.0"
1560
+ },
1561
+ "engines": {
1562
+ "node": ">= 18"
1563
+ }
1564
+ },
1565
+ "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": {
1566
+ "version": "24.2.0",
1567
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1568
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1569
+ "license": "MIT"
1570
+ },
1571
+ "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/types": {
1572
+ "version": "13.10.0",
1573
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1574
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1575
+ "license": "MIT",
1576
+ "dependencies": {
1577
+ "@octokit/openapi-types": "^24.2.0"
1578
+ }
1579
+ },
1580
+ "node_modules/@octokit/auth-oauth-user": {
1581
+ "version": "4.1.0",
1582
+ "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz",
1583
+ "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==",
1584
+ "license": "MIT",
1585
+ "dependencies": {
1586
+ "@octokit/auth-oauth-device": "^6.1.0",
1587
+ "@octokit/oauth-methods": "^4.1.0",
1588
+ "@octokit/request": "^8.3.1",
1589
+ "@octokit/types": "^13.0.0",
1590
+ "btoa-lite": "^1.0.0",
1591
+ "universal-user-agent": "^6.0.0"
1592
+ },
1593
+ "engines": {
1594
+ "node": ">= 18"
1595
+ }
1596
+ },
1597
+ "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": {
1598
+ "version": "24.2.0",
1599
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1600
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1601
+ "license": "MIT"
1602
+ },
1603
+ "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/types": {
1604
+ "version": "13.10.0",
1605
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1606
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1607
+ "license": "MIT",
1608
+ "dependencies": {
1609
+ "@octokit/openapi-types": "^24.2.0"
1610
+ }
1611
+ },
1612
+ "node_modules/@octokit/auth-token": {
1613
+ "version": "4.0.0",
1614
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
1615
+ "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
1616
+ "license": "MIT",
1617
+ "engines": {
1618
+ "node": ">= 18"
1619
+ }
1620
+ },
1621
+ "node_modules/@octokit/auth-unauthenticated": {
1622
+ "version": "5.0.1",
1623
+ "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz",
1624
+ "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==",
1625
+ "license": "MIT",
1626
+ "dependencies": {
1627
+ "@octokit/request-error": "^5.0.0",
1628
+ "@octokit/types": "^12.0.0"
1629
+ },
1630
+ "engines": {
1631
+ "node": ">= 18"
1632
+ }
1633
+ },
1634
+ "node_modules/@octokit/core": {
1635
+ "version": "5.2.1",
1636
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
1637
+ "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
1638
+ "license": "MIT",
1639
+ "dependencies": {
1640
+ "@octokit/auth-token": "^4.0.0",
1641
+ "@octokit/graphql": "^7.1.0",
1642
+ "@octokit/request": "^8.4.1",
1643
+ "@octokit/request-error": "^5.1.1",
1644
+ "@octokit/types": "^13.0.0",
1645
+ "before-after-hook": "^2.2.0",
1646
+ "universal-user-agent": "^6.0.0"
1647
+ },
1648
+ "engines": {
1649
+ "node": ">= 18"
1650
+ }
1651
+ },
1652
+ "node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
1653
+ "version": "24.2.0",
1654
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1655
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1656
+ "license": "MIT"
1657
+ },
1658
+ "node_modules/@octokit/core/node_modules/@octokit/types": {
1659
+ "version": "13.10.0",
1660
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1661
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1662
+ "license": "MIT",
1663
+ "dependencies": {
1664
+ "@octokit/openapi-types": "^24.2.0"
1665
+ }
1666
+ },
1667
+ "node_modules/@octokit/endpoint": {
1668
+ "version": "9.0.6",
1669
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
1670
+ "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
1671
+ "license": "MIT",
1672
+ "dependencies": {
1673
+ "@octokit/types": "^13.1.0",
1674
+ "universal-user-agent": "^6.0.0"
1675
+ },
1676
+ "engines": {
1677
+ "node": ">= 18"
1678
+ }
1679
+ },
1680
+ "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
1681
+ "version": "24.2.0",
1682
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1683
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1684
+ "license": "MIT"
1685
+ },
1686
+ "node_modules/@octokit/endpoint/node_modules/@octokit/types": {
1687
+ "version": "13.10.0",
1688
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1689
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1690
+ "license": "MIT",
1691
+ "dependencies": {
1692
+ "@octokit/openapi-types": "^24.2.0"
1693
+ }
1694
+ },
1695
+ "node_modules/@octokit/graphql": {
1696
+ "version": "7.1.1",
1697
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
1698
+ "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
1699
+ "license": "MIT",
1700
+ "dependencies": {
1701
+ "@octokit/request": "^8.4.1",
1702
+ "@octokit/types": "^13.0.0",
1703
+ "universal-user-agent": "^6.0.0"
1704
+ },
1705
+ "engines": {
1706
+ "node": ">= 18"
1707
+ }
1708
+ },
1709
+ "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
1710
+ "version": "24.2.0",
1711
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1712
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1713
+ "license": "MIT"
1714
+ },
1715
+ "node_modules/@octokit/graphql/node_modules/@octokit/types": {
1716
+ "version": "13.10.0",
1717
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1718
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1719
+ "license": "MIT",
1720
+ "dependencies": {
1721
+ "@octokit/openapi-types": "^24.2.0"
1722
+ }
1723
+ },
1724
+ "node_modules/@octokit/oauth-app": {
1725
+ "version": "6.1.0",
1726
+ "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz",
1727
+ "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==",
1728
+ "license": "MIT",
1729
+ "dependencies": {
1730
+ "@octokit/auth-oauth-app": "^7.0.0",
1731
+ "@octokit/auth-oauth-user": "^4.0.0",
1732
+ "@octokit/auth-unauthenticated": "^5.0.0",
1733
+ "@octokit/core": "^5.0.0",
1734
+ "@octokit/oauth-authorization-url": "^6.0.2",
1735
+ "@octokit/oauth-methods": "^4.0.0",
1736
+ "@types/aws-lambda": "^8.10.83",
1737
+ "universal-user-agent": "^6.0.0"
1738
+ },
1739
+ "engines": {
1740
+ "node": ">= 18"
1741
+ }
1742
+ },
1743
+ "node_modules/@octokit/oauth-authorization-url": {
1744
+ "version": "6.0.2",
1745
+ "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz",
1746
+ "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==",
1747
+ "license": "MIT",
1748
+ "engines": {
1749
+ "node": ">= 18"
1750
+ }
1751
+ },
1752
+ "node_modules/@octokit/oauth-methods": {
1753
+ "version": "4.1.0",
1754
+ "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz",
1755
+ "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==",
1756
+ "license": "MIT",
1757
+ "dependencies": {
1758
+ "@octokit/oauth-authorization-url": "^6.0.2",
1759
+ "@octokit/request": "^8.3.1",
1760
+ "@octokit/request-error": "^5.1.0",
1761
+ "@octokit/types": "^13.0.0",
1762
+ "btoa-lite": "^1.0.0"
1763
+ },
1764
+ "engines": {
1765
+ "node": ">= 18"
1766
+ }
1767
+ },
1768
+ "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": {
1769
+ "version": "24.2.0",
1770
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1771
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1772
+ "license": "MIT"
1773
+ },
1774
+ "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": {
1775
+ "version": "13.10.0",
1776
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1777
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1778
+ "license": "MIT",
1779
+ "dependencies": {
1780
+ "@octokit/openapi-types": "^24.2.0"
1781
+ }
1782
+ },
1783
+ "node_modules/@octokit/openapi-types": {
1784
+ "version": "20.0.0",
1785
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
1786
+ "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
1787
+ "license": "MIT"
1788
+ },
1789
+ "node_modules/@octokit/plugin-paginate-rest": {
1790
+ "version": "9.2.2",
1791
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
1792
+ "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
1793
+ "license": "MIT",
1794
+ "dependencies": {
1795
+ "@octokit/types": "^12.6.0"
1796
+ },
1797
+ "engines": {
1798
+ "node": ">= 18"
1799
+ },
1800
+ "peerDependencies": {
1801
+ "@octokit/core": "5"
1802
+ }
1803
+ },
1804
+ "node_modules/@octokit/request": {
1805
+ "version": "8.4.1",
1806
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
1807
+ "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
1808
+ "license": "MIT",
1809
+ "dependencies": {
1810
+ "@octokit/endpoint": "^9.0.6",
1811
+ "@octokit/request-error": "^5.1.1",
1812
+ "@octokit/types": "^13.1.0",
1813
+ "universal-user-agent": "^6.0.0"
1814
+ },
1815
+ "engines": {
1816
+ "node": ">= 18"
1817
+ }
1818
+ },
1819
+ "node_modules/@octokit/request-error": {
1820
+ "version": "5.1.1",
1821
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
1822
+ "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
1823
+ "license": "MIT",
1824
+ "dependencies": {
1825
+ "@octokit/types": "^13.1.0",
1826
+ "deprecation": "^2.0.0",
1827
+ "once": "^1.4.0"
1828
+ },
1829
+ "engines": {
1830
+ "node": ">= 18"
1831
+ }
1832
+ },
1833
+ "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
1834
+ "version": "24.2.0",
1835
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1836
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1837
+ "license": "MIT"
1838
+ },
1839
+ "node_modules/@octokit/request-error/node_modules/@octokit/types": {
1840
+ "version": "13.10.0",
1841
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1842
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1843
+ "license": "MIT",
1844
+ "dependencies": {
1845
+ "@octokit/openapi-types": "^24.2.0"
1846
+ }
1847
+ },
1848
+ "node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
1849
+ "version": "24.2.0",
1850
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
1851
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
1852
+ "license": "MIT"
1853
+ },
1854
+ "node_modules/@octokit/request/node_modules/@octokit/types": {
1855
+ "version": "13.10.0",
1856
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
1857
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
1858
+ "license": "MIT",
1859
+ "dependencies": {
1860
+ "@octokit/openapi-types": "^24.2.0"
1861
+ }
1862
+ },
1863
+ "node_modules/@octokit/types": {
1864
+ "version": "12.6.0",
1865
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
1866
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
1867
+ "license": "MIT",
1868
+ "dependencies": {
1869
+ "@octokit/openapi-types": "^20.0.0"
1870
+ }
1871
+ },
1872
+ "node_modules/@octokit/webhooks": {
1873
+ "version": "12.3.1",
1874
+ "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.3.1.tgz",
1875
+ "integrity": "sha512-BVwtWE3rRXB9IugmQTfKspqjNa8q+ab73ddkV9k1Zok3XbuOxJUi4lTYk5zBZDhfWb/Y2H+RO9Iggm25gsqeow==",
1876
+ "license": "MIT",
1877
+ "dependencies": {
1878
+ "@octokit/request-error": "^5.0.0",
1879
+ "@octokit/webhooks-methods": "^4.1.0",
1880
+ "@octokit/webhooks-types": "7.6.1",
1881
+ "aggregate-error": "^3.1.0"
1882
+ },
1883
+ "engines": {
1884
+ "node": ">= 18"
1885
+ }
1886
+ },
1887
+ "node_modules/@octokit/webhooks-methods": {
1888
+ "version": "4.1.0",
1889
+ "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz",
1890
+ "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==",
1891
+ "license": "MIT",
1892
+ "engines": {
1893
+ "node": ">= 18"
1894
+ }
1895
+ },
1896
+ "node_modules/@octokit/webhooks-types": {
1897
+ "version": "7.6.1",
1898
+ "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.6.1.tgz",
1899
+ "integrity": "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw==",
1900
+ "license": "MIT"
1901
+ },
1902
  "node_modules/@pkgjs/parseargs": {
1903
  "version": "0.11.0",
1904
  "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
 
2496
  "@types/estree": "*"
2497
  }
2498
  },
2499
+ "node_modules/@types/aws-lambda": {
2500
+ "version": "8.10.149",
2501
+ "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.149.tgz",
2502
+ "integrity": "sha512-NXSZIhfJjnXqJgtS7IwutqIF/SOy1Wz5Px4gUY1RWITp3AYTyuJS4xaXr/bIJY1v15XMzrJ5soGnPM+7uigZjA==",
2503
+ "license": "MIT"
2504
+ },
2505
+ "node_modules/@types/btoa-lite": {
2506
+ "version": "1.0.2",
2507
+ "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz",
2508
+ "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==",
2509
+ "license": "MIT"
2510
+ },
2511
  "node_modules/@types/cookie": {
2512
  "version": "0.6.0",
2513
  "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
 
2565
  "dev": true,
2566
  "license": "MIT"
2567
  },
2568
+ "node_modules/@types/jsonwebtoken": {
2569
+ "version": "9.0.9",
2570
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz",
2571
+ "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==",
2572
+ "license": "MIT",
2573
+ "dependencies": {
2574
+ "@types/ms": "*",
2575
+ "@types/node": "*"
2576
+ }
2577
+ },
2578
  "node_modules/@types/mdast": {
2579
  "version": "3.0.15",
2580
  "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
 
2596
  "version": "2.1.0",
2597
  "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
2598
  "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
 
2599
  "license": "MIT"
2600
  },
2601
  "node_modules/@types/node": {
2602
  "version": "22.15.29",
2603
  "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
2604
  "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
 
2605
  "license": "MIT",
2606
  "dependencies": {
2607
  "undici-types": "~6.21.0"
 
3765
  "version": "3.1.0",
3766
  "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
3767
  "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
 
3768
  "license": "MIT",
3769
  "dependencies": {
3770
  "clean-stack": "^2.0.0",
 
4194
  "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
4195
  "license": "MIT"
4196
  },
4197
+ "node_modules/before-after-hook": {
4198
+ "version": "2.2.3",
4199
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
4200
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
4201
+ "license": "Apache-2.0"
4202
+ },
4203
  "node_modules/binary-extensions": {
4204
  "version": "2.3.0",
4205
  "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
 
4328
  "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
4329
  }
4330
  },
4331
+ "node_modules/btoa-lite": {
4332
+ "version": "1.0.0",
4333
+ "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
4334
+ "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==",
4335
+ "license": "MIT"
4336
+ },
4337
  "node_modules/buffer": {
4338
  "version": "5.7.1",
4339
  "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
 
4359
  "ieee754": "^1.1.13"
4360
  }
4361
  },
4362
+ "node_modules/buffer-equal-constant-time": {
4363
+ "version": "1.0.1",
4364
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
4365
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
4366
+ "license": "BSD-3-Clause"
4367
+ },
4368
  "node_modules/buffer-from": {
4369
  "version": "1.1.2",
4370
  "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
 
4622
  "version": "2.2.0",
4623
  "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
4624
  "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
 
4625
  "license": "MIT",
4626
  "engines": {
4627
  "node": ">=6"
 
5078
  "node": ">= 0.8"
5079
  }
5080
  },
5081
+ "node_modules/deprecation": {
5082
+ "version": "2.3.1",
5083
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
5084
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
5085
+ "license": "ISC"
5086
+ },
5087
  "node_modules/dequal": {
5088
  "version": "2.0.3",
5089
  "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
 
5241
  "dev": true,
5242
  "license": "MIT"
5243
  },
5244
+ "node_modules/ecdsa-sig-formatter": {
5245
+ "version": "1.0.11",
5246
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
5247
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
5248
+ "license": "Apache-2.0",
5249
+ "dependencies": {
5250
+ "safe-buffer": "^5.0.1"
5251
+ }
5252
+ },
5253
  "node_modules/ee-first": {
5254
  "version": "1.1.1",
5255
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
 
7237
  "version": "4.0.0",
7238
  "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
7239
  "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
 
7240
  "license": "MIT",
7241
  "engines": {
7242
  "node": ">=8"
 
8044
  "graceful-fs": "^4.1.6"
8045
  }
8046
  },
8047
+ "node_modules/jsonwebtoken": {
8048
+ "version": "9.0.2",
8049
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
8050
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
8051
+ "license": "MIT",
8052
+ "dependencies": {
8053
+ "jws": "^3.2.2",
8054
+ "lodash.includes": "^4.3.0",
8055
+ "lodash.isboolean": "^3.0.3",
8056
+ "lodash.isinteger": "^4.0.4",
8057
+ "lodash.isnumber": "^3.0.3",
8058
+ "lodash.isplainobject": "^4.0.6",
8059
+ "lodash.isstring": "^4.0.1",
8060
+ "lodash.once": "^4.0.0",
8061
+ "ms": "^2.1.1",
8062
+ "semver": "^7.5.4"
8063
+ },
8064
+ "engines": {
8065
+ "node": ">=12",
8066
+ "npm": ">=6"
8067
+ }
8068
+ },
8069
  "node_modules/jsx-ast-utils": {
8070
  "version": "3.3.5",
8071
  "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
 
8082
  "node": ">=4.0"
8083
  }
8084
  },
8085
+ "node_modules/jwa": {
8086
+ "version": "1.4.2",
8087
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
8088
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
8089
+ "license": "MIT",
8090
+ "dependencies": {
8091
+ "buffer-equal-constant-time": "^1.0.1",
8092
+ "ecdsa-sig-formatter": "1.0.11",
8093
+ "safe-buffer": "^5.0.1"
8094
+ }
8095
+ },
8096
+ "node_modules/jws": {
8097
+ "version": "3.2.2",
8098
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
8099
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
8100
+ "license": "MIT",
8101
+ "dependencies": {
8102
+ "jwa": "^1.4.1",
8103
+ "safe-buffer": "^5.0.1"
8104
+ }
8105
+ },
8106
  "node_modules/keyv": {
8107
  "version": "4.5.4",
8108
  "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
 
8242
  "dev": true,
8243
  "license": "MIT"
8244
  },
8245
+ "node_modules/lodash.includes": {
8246
+ "version": "4.3.0",
8247
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
8248
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
8249
+ "license": "MIT"
8250
+ },
8251
+ "node_modules/lodash.isboolean": {
8252
+ "version": "3.0.3",
8253
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
8254
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
8255
+ "license": "MIT"
8256
+ },
8257
+ "node_modules/lodash.isinteger": {
8258
+ "version": "4.0.4",
8259
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
8260
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
8261
+ "license": "MIT"
8262
+ },
8263
+ "node_modules/lodash.isnumber": {
8264
+ "version": "3.0.3",
8265
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
8266
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
8267
+ "license": "MIT"
8268
+ },
8269
+ "node_modules/lodash.isplainobject": {
8270
+ "version": "4.0.6",
8271
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
8272
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
8273
+ "license": "MIT"
8274
+ },
8275
+ "node_modules/lodash.isstring": {
8276
+ "version": "4.0.1",
8277
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
8278
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
8279
+ "license": "MIT"
8280
+ },
8281
  "node_modules/lodash.merge": {
8282
  "version": "4.6.2",
8283
  "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
 
8285
  "dev": true,
8286
  "license": "MIT"
8287
  },
8288
+ "node_modules/lodash.once": {
8289
+ "version": "4.1.1",
8290
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
8291
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
8292
+ "license": "MIT"
8293
+ },
8294
  "node_modules/log-symbols": {
8295
  "version": "4.1.0",
8296
  "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
 
9961
  "version": "1.4.0",
9962
  "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
9963
  "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
 
9964
  "license": "ISC",
9965
  "dependencies": {
9966
  "wrappy": "1"
 
11412
  "version": "7.7.2",
11413
  "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
11414
  "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
 
11415
  "license": "ISC",
11416
  "bin": {
11417
  "semver": "bin/semver.js"
 
12720
  "version": "6.21.0",
12721
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
12722
  "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
 
12723
  "license": "MIT"
12724
  },
12725
  "node_modules/unified": {
 
12894
  "url": "https://opencollective.com/unified"
12895
  }
12896
  },
12897
+ "node_modules/universal-github-app-jwt": {
12898
+ "version": "1.2.0",
12899
+ "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz",
12900
+ "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==",
12901
+ "license": "MIT",
12902
+ "dependencies": {
12903
+ "@types/jsonwebtoken": "^9.0.0",
12904
+ "jsonwebtoken": "^9.0.2"
12905
+ }
12906
+ },
12907
+ "node_modules/universal-user-agent": {
12908
+ "version": "6.0.1",
12909
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
12910
+ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
12911
+ "license": "ISC"
12912
+ },
12913
  "node_modules/universalify": {
12914
  "version": "2.0.1",
12915
  "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
 
13946
  "version": "1.0.2",
13947
  "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
13948
  "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
 
13949
  "license": "ISC"
13950
  },
13951
  "node_modules/ws": {
package.json CHANGED
@@ -8,12 +8,18 @@
8
  "dev": "remix vite:dev",
9
  "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
10
  "start": "remix-serve ./build/server/index.js",
11
- "typecheck": "tsc"
 
 
12
  },
13
  "dependencies": {
14
  "@remix-run/node": "^2.16.8",
15
  "@remix-run/react": "^2.16.8",
16
  "@remix-run/serve": "^2.16.8",
 
 
 
 
17
  "isbot": "^4.1.0",
18
  "react": "^18.2.0",
19
  "react-dom": "^18.2.0"
@@ -22,6 +28,7 @@
22
  "@remix-run/dev": "^2.16.8",
23
  "@types/react": "^18.2.20",
24
  "@types/react-dom": "^18.2.7",
 
25
  "@typescript-eslint/eslint-plugin": "^6.7.4",
26
  "@typescript-eslint/parser": "^6.7.4",
27
  "autoprefixer": "^10.4.19",
 
8
  "dev": "remix vite:dev",
9
  "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
10
  "start": "remix-serve ./build/server/index.js",
11
+ "typecheck": "tsc",
12
+ "setup": "node generate-secrets.mjs",
13
+ "secrets": "node generate-secrets.mjs"
14
  },
15
  "dependencies": {
16
  "@remix-run/node": "^2.16.8",
17
  "@remix-run/react": "^2.16.8",
18
  "@remix-run/serve": "^2.16.8",
19
+ "@octokit/app": "^14.0.2",
20
+ "@octokit/auth-app": "^6.0.1",
21
+ "@octokit/webhooks": "^12.0.4",
22
+ "jsonwebtoken": "^9.0.2",
23
  "isbot": "^4.1.0",
24
  "react": "^18.2.0",
25
  "react-dom": "^18.2.0"
 
28
  "@remix-run/dev": "^2.16.8",
29
  "@types/react": "^18.2.20",
30
  "@types/react-dom": "^18.2.7",
31
+ "@types/jsonwebtoken": "^9.0.5",
32
  "@typescript-eslint/eslint-plugin": "^6.7.4",
33
  "@typescript-eslint/parser": "^6.7.4",
34
  "autoprefixer": "^10.4.19",