diff --git a/codex-cli/package.json b/codex-cli/package.json index 524e4065..2b3d8568 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -31,6 +31,7 @@ "chalk": "^5.2.0", "diff": "^7.0.0", "dotenv": "^16.1.4", + "express": "^5.1.0", "fast-deep-equal": "^3.1.3", "fast-npm-meta": "^0.4.2", "figures": "^6.1.0", @@ -54,6 +55,7 @@ "devDependencies": { "@eslint/js": "^9.22.0", "@types/diff": "^7.0.2", + "@types/express": "^5.0.1", "@types/js-yaml": "^4.0.9", "@types/marked-terminal": "^6.1.1", "@types/react": "^18.0.32", diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index d4966f0f..bf9385f8 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -20,11 +20,11 @@ import { ReviewDecision } from "./utils/agent/review"; import { AutoApprovalMode } from "./utils/auto-approval-mode"; import { checkForUpdates } from "./utils/check-updates"; import { - getApiKey, loadConfig, PRETTY_PRINT, INSTRUCTIONS_FILEPATH, } from "./utils/config"; +import { getApiKey as fetchApiKey } from "./utils/get-api-key"; import { createInputItem } from "./utils/input-utils"; import { initLogger } from "./utils/logger/log"; import { isModelSupportedForResponses } from "./utils/model-utils.js"; @@ -35,6 +35,7 @@ import { spawnSync } from "child_process"; import fs from "fs"; import { render } from "ink"; import meow from "meow"; +import os from "os"; import path from "path"; import React from "react"; @@ -271,7 +272,38 @@ let prompt = cli.input[0]; const model = cli.flags.model ?? config.model; const imagePaths = cli.flags.image; const provider = cli.flags.provider ?? config.provider ?? "openai"; -const apiKey = getApiKey(provider); + +const client = { + issuer: "https://auth.openai.com", + client_id: "app_EMoamEEZ73f0CkXaXp7hrann", +}; + +let apiKey = ""; + +// Try to load existing auth file if present +try { + const home = os.homedir(); + const authDir = path.join(home, ".codex"); + const authFile = path.join(authDir, "auth.json"); + if (fs.existsSync(authFile)) { + const data = JSON.parse(fs.readFileSync(authFile, "utf-8")); + const lastRefreshTime = data.last_refresh + ? new Date(data.last_refresh).getTime() + : 0; + const expired = Date.now() - lastRefreshTime > 28 * 24 * 60 * 60 * 1000; + if (data.OPENAI_API_KEY && !expired) { + apiKey = data.OPENAI_API_KEY; + } + } +} catch { + // ignore errors +} + +if (!apiKey) { + apiKey = await fetchApiKey(client.issuer, client.client_id); +} +// Ensure the API key is available as an environment variable for legacy code +process.env["OPENAI_API_KEY"] = apiKey; // Set of providers that don't require API keys const NO_API_KEY_REQUIRED = new Set(["ollama"]); diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 9198e7fd..cc57239b 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -17,7 +17,6 @@ import { OPENAI_TIMEOUT_MS, OPENAI_ORGANIZATION, OPENAI_PROJECT, - getApiKey, getBaseUrl, AZURE_OPENAI_API_VERSION, } from "../config.js"; @@ -307,7 +306,7 @@ export class AgentLoop { this.sessionId = getSessionId() || randomUUID().replaceAll("-", ""); // Configure OpenAI client with optional timeout (ms) from environment const timeoutMs = OPENAI_TIMEOUT_MS; - const apiKey = getApiKey(this.provider); + const apiKey = this.config.apiKey ?? process.env["OPENAI_API_KEY"] ?? ""; const baseURL = getBaseUrl(this.provider); this.oai = new OpenAI({ @@ -766,7 +765,7 @@ export class AgentLoop { // prompts) and so that freshly generated `function_call_output`s are // shown immediately. // Figure out what subset of `turnInput` constitutes *new* information - // for the UI so that we don’t spam the interface with repeats of the + // for the UI so that we don't spam the interface with repeats of the // entire transcript on every iteration when response storage is // disabled. const deltaInput = this.disableResponseStorage @@ -1645,7 +1644,6 @@ You MUST adhere to the following criteria when executing the task: - If there is a .pre-commit-config.yaml, use \`pre-commit run --files ...\` to check that your changes pass the pre-commit checks. However, do not fix pre-existing errors on lines you didn't touch. - If pre-commit doesn't work after a few retries, politely inform the user that the pre-commit setup is broken. - Once you finish coding, you must - - Check \`git status\` to sanity check your changes; revert any scratch files or changes. - Remove all inline comments you added as much as possible, even if they look normal. Check using \`git diff\`. Inline comments must be generally avoided, unless active maintainers of the repo, after long careful study of the code and the issue, will still misinterpret the code without the comments. - Check if you accidentally add copyright or license headers. If so, remove them. - Try to run pre-commit if it is available. diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index 95183937..51761bf6 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -120,7 +120,7 @@ export function getApiKey(provider: string = "openai"): string | undefined { return process.env[providerInfo.envKey]; } - // Checking `PROVIDER_API_KEY feels more intuitive with a custom provider. + // Checking `PROVIDER_API_KEY` feels more intuitive with a custom provider. const customApiKey = process.env[`${provider.toUpperCase()}_API_KEY`]; if (customApiKey) { return customApiKey; diff --git a/codex-cli/src/utils/get-api-key-components.tsx b/codex-cli/src/utils/get-api-key-components.tsx new file mode 100644 index 00000000..45346632 --- /dev/null +++ b/codex-cli/src/utils/get-api-key-components.tsx @@ -0,0 +1,75 @@ +import SelectInput from "../components/select-input/select-input.js"; +import Spinner from "../components/vendor/ink-spinner.js"; +import TextInput from "../components/vendor/ink-text-input.js"; +import { Box, Text } from "ink"; +import React, { useState } from "react"; + +export type Choice = { type: "signin" } | { type: "apikey"; key: string }; + +export function ApiKeyPrompt({ + onDone, +}: { + onDone: (choice: Choice) => void; +}): JSX.Element { + const [step, setStep] = useState<"select" | "paste">("select"); + const [apiKey, setApiKey] = useState(""); + + if (step === "select") { + return ( + + + + Sign in with ChatGPT to generate an API key or paste one you already + have. + + [use arrows to move, enter to select] + + { + if (item.value === "signin") { + onDone({ type: "signin" }); + } else { + setStep("paste"); + } + }} + /> + + ); + } + + return ( + + Paste your OpenAI API key and press <Enter>: + { + if (value.trim() !== "") { + onDone({ type: "apikey", key: value.trim() }); + } + }} + placeholder="sk-..." + mask="*" + /> + + ); +} + +export function WaitingForAuth(): JSX.Element { + return ( + + + + {" "} + Waiting for authentication… ctrl + c to quit + + + ); +} diff --git a/codex-cli/src/utils/get-api-key.tsx b/codex-cli/src/utils/get-api-key.tsx new file mode 100644 index 00000000..0dd52f73 --- /dev/null +++ b/codex-cli/src/utils/get-api-key.tsx @@ -0,0 +1,498 @@ +import type { Choice } from "./get-api-key-components"; +import type { Request, Response } from "express"; + +import { ApiKeyPrompt, WaitingForAuth } from "./get-api-key-components"; +import { clearTerminal } from "./terminal"; +import express from "express"; +import fs from "fs/promises"; +import { render } from "ink"; +import crypto from "node:crypto"; +import { URL } from "node:url"; +import open from "open"; +import os from "os"; +import path from "path"; +import React from "react"; + +function promptUserForChoice(): Promise { + return new Promise((resolve) => { + const instance = render( + { + resolve(choice); + instance.unmount(); + }} + />, + ); + }); +} + +interface OidcConfiguration { + issuer: string; + authorization_endpoint: string; + token_endpoint: string; +} + +async function getOidcConfiguration( + issuer: string, +): Promise { + const discoveryUrl = new URL(issuer); + discoveryUrl.pathname = "/.well-known/openid-configuration"; + + if (issuer === "https://auth.openai.com") { + // Account for legacy quirk in production tenant + discoveryUrl.pathname = "/v2.0" + discoveryUrl.pathname; + } + + const res = await fetch(discoveryUrl.toString()); + if (!res.ok) { + throw new Error("Failed to fetch OIDC configuration"); + } + return (await res.json()) as OidcConfiguration; +} + +interface IDTokenClaims { + "https://api.openai.com/auth": { + organization_id: string; + project_id: string; + completed_platform_onboarding: boolean; + is_org_owner: boolean; + }; +} + +interface AccessTokenClaims { + "https://api.openai.com/auth": { + chatgpt_plan_type: string; + }; +} + +function generatePKCECodes(): { + code_verifier: string; + code_challenge: string; +} { + const code_verifier = crypto.randomBytes(64).toString("hex"); + const code_challenge = crypto + .createHash("sha256") + .update(code_verifier) + .digest("base64url"); + return { code_verifier, code_challenge }; +} + +async function handleCallback( + req: Request, + issuer: string, + oidcConfig: OidcConfiguration, + codeVerifier: string, + clientId: string, + redirectUri: string, + expectedState: string, +): Promise<{ access_token: string; success_url: string }> { + const state = (req.query as Record)["state"] as + | string + | undefined; + if (!state || state !== expectedState) { + throw new Error("Invalid state parameter"); + } + + const code = (req.query as Record)["code"] as + | string + | undefined; + if (!code) { + throw new Error("Missing authorization code"); + } + + const params = new URLSearchParams(); + params.append("grant_type", "authorization_code"); + params.append("code", code); + params.append("redirect_uri", redirectUri); + params.append("client_id", clientId); + params.append("code_verifier", codeVerifier); + + oidcConfig.token_endpoint = `${issuer}/oauth/token`; + const tokenRes = await fetch(oidcConfig.token_endpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: params.toString(), + }); + + if (!tokenRes.ok) { + throw new Error("Failed to exchange authorization code for tokens"); + } + + const tokenData = (await tokenRes.json()) as { + access_token: string; + id_token: string; + refresh_token?: string; + }; + + const idTokenParts = tokenData.id_token.split("."); + if (idTokenParts.length !== 3) { + throw new Error("Invalid ID token"); + } + const accessTokenParts = tokenData.access_token.split("."); + if (accessTokenParts.length !== 3) { + throw new Error("Invalid access token"); + } + + const idTokenClaims = JSON.parse( + Buffer.from(idTokenParts[1]!, "base64url").toString("utf8"), + ) as IDTokenClaims; + + const accessTokenClaims = JSON.parse( + Buffer.from(accessTokenParts[1]!, "base64url").toString("utf8"), + ) as AccessTokenClaims; + + const org_id = idTokenClaims["https://api.openai.com/auth"]?.organization_id; + + if (!org_id) { + throw new Error("Missing organization in id_token claims"); + } + const project_id = idTokenClaims["https://api.openai.com/auth"]?.project_id; + + if (!project_id) { + throw new Error("Missing project in id_token claims"); + } + + const randomId = crypto.randomBytes(6).toString("hex"); + const exchangeParams = new URLSearchParams({ + grant_type: "urn:ietf:params:oauth:grant-type:token-exchange", + client_id: clientId, + requested_token: "openai-api-key", + subject_token: tokenData.id_token, + subject_token_type: "urn:ietf:params:oauth:token-type:id_token", + name: `Codex CLI [auto-generated] (${new Date().toISOString().slice(0, 10)}) [${ + randomId + }]`, + }); + const exchangeRes = await fetch(oidcConfig.token_endpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: exchangeParams.toString(), + }); + if (!exchangeRes.ok) { + throw new Error(`Failed to create API key: ${await exchangeRes.text()}`); + } + + const exchanged = (await exchangeRes.json()) as { + access_token: string; + key: string; + }; + + // Determine whether the organization still requires additional + // setup (e.g., adding a payment method) based on the ID-token + // claim provided by the auth service. + const completedOnboarding = Boolean( + idTokenClaims["https://api.openai.com/auth"]?.completed_platform_onboarding, + ); + const chatgptPlanType = + accessTokenClaims["https://api.openai.com/auth"]?.chatgpt_plan_type; + let needsSetup = false; + if (chatgptPlanType === "plus" || chatgptPlanType === "pro") { + needsSetup = !completedOnboarding; + } + + // Build the success URL on the same host/port as the callback and + // include the required query parameters for the front-end page. + // console.log("Redirecting to success page"); + const successUrl = new URL("/success", redirectUri); + if (issuer === "https://auth.openai.com") { + successUrl.searchParams.set("platform_url", "https://platform.openai.com"); + } else { + successUrl.searchParams.set( + "platform_url", + "https://platform.api.openai.org", + ); + } + successUrl.searchParams.set("id_token", tokenData.id_token); + successUrl.searchParams.set("needs_setup", needsSetup ? "true" : "false"); + successUrl.searchParams.set("org_id", org_id); + successUrl.searchParams.set("project_id", project_id); + successUrl.searchParams.set("plan_type", chatgptPlanType); + + try { + const home = os.homedir(); + const authDir = path.join(home, ".codex"); + await fs.mkdir(authDir, { recursive: true }); + const authFile = path.join(authDir, "auth.json"); + const authData = { + tokens: tokenData, + last_refresh: new Date().toISOString(), + OPENAI_API_KEY: exchanged.access_token, + }; + await fs.writeFile(authFile, JSON.stringify(authData, null, 2), { + mode: 0o600, + }); + } catch (err) { + // eslint-disable-next-line no-console + console.warn("Unable to save auth file:", err); + } + + return { + access_token: exchanged.access_token, + success_url: successUrl.toString(), + }; +} + +const LOGIN_SUCCESS_HTML = String.raw` + + + + Sign into Codex CLI + + + + + + + + + + + + + Signed in to Codex CLI + + + You may now close this page + + + + +`; + +async function signInFlow(issuer: string, clientId: string): Promise { + const app = express(); + + let codeVerifier = ""; + let redirectUri = ""; + let server: ReturnType; + const state = crypto.randomBytes(32).toString("hex"); + + const apiKeyPromise = new Promise((resolve, reject) => { + let _apiKey: string | undefined; + + app.get("/success", (_req: Request, res: Response) => { + res.type("text/html").send(LOGIN_SUCCESS_HTML); + if (_apiKey) { + resolve(_apiKey); + } else { + // eslint-disable-next-line no-console + console.error( + "Sorry, it seems like the authentication flow failed. Please try again, or submit an issue on our GitHub if it continues.", + ); + process.exit(1); + } + }); + + // Callback route ------------------------------------------------------- + app.get("/auth/callback", async (req: Request, res: Response) => { + try { + const oidcConfig = await getOidcConfiguration(issuer); + oidcConfig.token_endpoint = `${issuer}/oauth/token`; + oidcConfig.authorization_endpoint = `${issuer}/oauth/authorize`; + const { access_token, success_url } = await handleCallback( + req, + issuer, + oidcConfig, + codeVerifier, + clientId, + redirectUri, + state, + ); + _apiKey = access_token; + res.redirect(success_url); + } catch (err) { + reject(err); + } + }); + + server = app.listen(1455, "127.0.0.1", async () => { + const address = server.address(); + if (typeof address === "string" || !address) { + // eslint-disable-next-line no-console + console.log( + "It seems like you might already be trying to sign in (port :1455 already in use)", + ); + process.exit(1); + return; + } + const port = address.port; + redirectUri = `http://localhost:${port}/auth/callback`; + + try { + const oidcConfig = await getOidcConfiguration(issuer); + oidcConfig.token_endpoint = `${issuer}/oauth/token`; + oidcConfig.authorization_endpoint = `${issuer}/oauth/authorize`; + const pkce = generatePKCECodes(); + codeVerifier = pkce.code_verifier; + + const authUrl = new URL(oidcConfig.authorization_endpoint); + authUrl.searchParams.append("response_type", "code"); + authUrl.searchParams.append("client_id", clientId); + authUrl.searchParams.append("redirect_uri", redirectUri); + authUrl.searchParams.append( + "scope", + "openid profile email offline_access", + ); + authUrl.searchParams.append("code_challenge", pkce.code_challenge); + authUrl.searchParams.append("code_challenge_method", "S256"); + authUrl.searchParams.append("id_token_add_organizations", "true"); + authUrl.searchParams.append("state", state); + + // Open the browser immediately. + open(authUrl.toString()); + setTimeout(() => { + // eslint-disable-next-line no-console + console.log( + `\nOpening login page in your browser: ${authUrl.toString()}\n`, + ); + }, 500); + } catch (err) { + reject(err); + } + }); + }); + + // Ensure the server is closed afterwards. + return apiKeyPromise.finally(() => { + if (server) { + server.close(); + } + }); +} + +export async function getApiKey( + issuer: string, + clientId: string, +): Promise { + if (process.env["OPENAI_API_KEY"]) { + return process.env["OPENAI_API_KEY"]!; + } + const choice = await promptUserForChoice(); + if (choice.type === "apikey") { + process.env["OPENAI_API_KEY"] = choice.key; + return choice.key; + } + const spinner = render(); + try { + const key = await signInFlow(issuer, clientId); + spinner.unmount(); + clearTerminal(); + process.env["OPENAI_API_KEY"] = key; + return key; + } catch (err) { + spinner.unmount(); + throw err; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00b8f63e..6e4db695 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,9 @@ importers: dotenv: specifier: ^16.1.4 version: 16.5.0 + express: + specifier: ^5.1.0 + version: 5.1.0 fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -109,6 +112,9 @@ importers: '@types/diff': specifier: ^7.0.2 version: 7.0.2 + '@types/express': + specifier: ^5.0.1 + version: 5.0.1 '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 @@ -538,15 +544,30 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/cardinal@2.1.1': resolution: {integrity: sha512-/xCVwg8lWvahHsV2wXZt4i64H1sdL+sN1Uoq7fAc8/FA6uYHjuIveDwPwvGUYp4VZiv85dVl6J/Bum3NDAOm8g==} + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/diff@7.0.2': resolution: {integrity: sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==} '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/express-serve-static-core@5.0.6': + resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} + + '@types/express@5.0.1': + resolution: {integrity: sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -556,6 +577,9 @@ packages: '@types/marked-terminal@6.1.1': resolution: {integrity: sha512-DfoUqkmFDCED7eBY9vFUhJ9fW8oZcMAK5EwRDQ9drjTbpQa+DnBTQQCwWhTFVf4WsZ6yYcJTI8D91wxTWXRZZQ==} + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} @@ -568,12 +592,24 @@ packages: '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + '@types/qs@6.9.18': + resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/react@18.3.20': resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} '@types/semver@7.7.0': resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/shell-quote@1.7.5': resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==} @@ -674,6 +710,10 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -789,6 +829,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + boxen@8.0.1: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} @@ -807,6 +851,10 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -908,10 +956,26 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-to-spaces@2.0.1: resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -986,6 +1050,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -1014,6 +1082,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -1023,6 +1094,10 @@ packages: emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -1074,6 +1149,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -1170,6 +1248,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -1185,6 +1267,10 @@ packages: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1231,6 +1317,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1257,6 +1347,14 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1395,6 +1493,10 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -1411,6 +1513,10 @@ packages: engines: {node: '>=18'} hasBin: true + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1463,6 +1569,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -1554,6 +1664,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1704,10 +1817,18 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1723,10 +1844,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -1763,6 +1892,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -1817,6 +1950,10 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -1880,6 +2017,10 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + patch-console@2.0.0: resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1903,6 +2044,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -1954,13 +2099,29 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2024,6 +2185,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + run-applescript@7.0.0: resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} engines: {node: '>=18'} @@ -2035,6 +2200,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -2043,6 +2211,9 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -2051,6 +2222,14 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -2063,6 +2242,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2128,6 +2310,10 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -2238,6 +2424,10 @@ packages: resolution: {integrity: sha512-KsEID8AfgUy+pxVRLsWp0VzCa69wxzUDZnzGbyIST/bcgcrMvTYoFBX/QORH4YApoD89EDuUovx4BTdpOn319Q==} engines: {node: '>=18'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + token-types@6.0.0: resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} engines: {node: '>=14.16'} @@ -2284,6 +2474,10 @@ packages: resolution: {integrity: sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==} engines: {node: '>=16'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -2323,6 +2517,10 @@ packages: resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} engines: {node: '>=4'} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2335,6 +2533,10 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vite-node@3.1.2: resolution: {integrity: sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2756,12 +2958,36 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.14.1 + '@types/cardinal@2.1.1': {} + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.14.1 + '@types/diff@7.0.2': {} '@types/estree@1.0.7': {} + '@types/express-serve-static-core@5.0.6': + dependencies: + '@types/node': 22.14.1 + '@types/qs': 6.9.18 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express@5.0.1': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 5.0.6 + '@types/serve-static': 1.15.7 + + '@types/http-errors@2.0.4': {} + '@types/js-yaml@4.0.9': {} '@types/json5@0.0.29': {} @@ -2773,6 +2999,8 @@ snapshots: chalk: 5.4.1 marked: 11.2.0 + '@types/mime@1.3.5': {} + '@types/node-fetch@2.6.12': dependencies: '@types/node': 22.14.1 @@ -2788,6 +3016,10 @@ snapshots: '@types/prop-types@15.7.14': {} + '@types/qs@6.9.18': {} + + '@types/range-parser@1.2.7': {} + '@types/react@18.3.20': dependencies: '@types/prop-types': 15.7.14 @@ -2795,6 +3027,17 @@ snapshots: '@types/semver@7.7.0': {} + '@types/send@0.17.4': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.14.1 + + '@types/serve-static@1.15.7': + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 22.14.1 + '@types/send': 0.17.4 + '@types/shell-quote@1.7.5': {} '@types/which@3.0.4': {} @@ -2926,6 +3169,11 @@ snapshots: dependencies: event-target-shim: 5.0.1 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: acorn: 8.14.1 @@ -3054,6 +3302,20 @@ snapshots: balanced-match@1.0.2: {} + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + boxen@8.0.1: dependencies: ansi-align: 3.0.1 @@ -3082,6 +3344,8 @@ snapshots: dependencies: run-applescript: 7.0.0 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -3182,8 +3446,18 @@ snapshots: concat-map@0.0.1: {} + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + convert-to-spaces@2.0.1: {} + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + create-require@1.1.1: {} cross-spawn@7.0.6: @@ -3249,6 +3523,8 @@ snapshots: delayed-stream@1.0.0: {} + depd@2.0.0: {} + diff@4.0.2: {} diff@7.0.0: {} @@ -3273,12 +3549,16 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + ee-first@1.1.1: {} + emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} emojilib@2.4.0: {} + encodeurl@2.0.0: {} + environment@1.1.0: {} es-abstract@1.23.9: @@ -3413,6 +3693,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@2.0.0: {} escape-string-regexp@4.0.0: {} @@ -3566,6 +3848,8 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + event-target-shim@5.0.1: {} eventemitter3@5.0.1: {} @@ -3584,6 +3868,38 @@ snapshots: expect-type@1.2.1: {} + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -3631,6 +3947,17 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@2.1.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3662,6 +3989,10 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -3802,6 +4133,14 @@ snapshots: highlight.js@10.7.3: {} + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 @@ -3817,6 +4156,10 @@ snapshots: husky@9.1.7: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -3880,6 +4223,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + ipaddr.js@1.9.1: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -3964,6 +4309,8 @@ snapshots: is-path-inside@3.0.3: {} + is-promise@4.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -4127,8 +4474,12 @@ snapshots: math-intrinsics@1.1.0: {} + media-typer@1.1.0: {} + meow@13.2.0: {} + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -4140,10 +4491,16 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} @@ -4172,6 +4529,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + node-domexception@1.0.0: {} node-emoji@2.2.0: @@ -4231,6 +4590,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -4306,6 +4669,8 @@ snapshots: parse5@6.0.1: {} + parseurl@1.3.3: {} + patch-console@2.0.0: {} path-exists@4.0.0: {} @@ -4318,6 +4683,8 @@ snapshots: path-parse@1.0.7: {} + path-to-regexp@8.2.0: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -4352,10 +4719,28 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + punycode@2.3.1: {} + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + queue-microtask@1.2.3: {} + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + react-is@16.13.1: {} react-reconciler@0.29.2(react@18.3.1): @@ -4448,6 +4833,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.40.0 fsevents: 2.3.3 + router@2.2.0: + dependencies: + debug: 4.4.0 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + run-applescript@7.0.0: {} run-parallel@1.2.0: @@ -4462,6 +4857,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -4473,12 +4870,39 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safer-buffer@2.1.2: {} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 semver@7.7.1: {} + send@1.2.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -4501,6 +4925,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -4567,6 +4993,8 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.1: {} + std-env@3.9.0: {} string-argv@0.3.2: {} @@ -4688,6 +5116,8 @@ snapshots: to-rotated@1.0.0: {} + toidentifier@1.0.1: {} + token-types@6.0.0: dependencies: '@tokenizer/token': 0.3.0 @@ -4736,6 +5166,12 @@ snapshots: type-fest@4.40.0: {} + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -4786,6 +5222,8 @@ snapshots: unicode-emoji-modifier-base@1.0.0: {} + unpipe@1.0.0: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -4796,6 +5234,8 @@ snapshots: v8-compile-cache-lib@3.0.1: {} + vary@1.1.2: {} + vite-node@3.1.2(@types/node@22.14.1)(yaml@2.7.1): dependencies: cac: 6.7.14