From 4631464de236a77eeb770e9bc11d4a6c01a40937 Mon Sep 17 00:00:00 2001 From: haruka <1628615876@qq.com> Date: Fri, 13 Feb 2026 06:39:25 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0WebUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 3 + Dockerfile.console | 39 ++ bun.lock | 1 + src/console/account-store.ts | 78 ++++ src/console/api.ts | 257 +++++++++++++ src/console/auth-flow.ts | 139 +++++++ src/console/index.ts | 132 +++++++ src/console/instance-manager.ts | 446 ++++++++++++++++++++++ src/console/port-check.ts | 74 ++++ src/main.ts | 9 +- web/bun.lock | 257 +++++++++++++ web/index.html | 12 + web/package.json | 22 ++ web/src/App.tsx | 108 ++++++ web/src/api.ts | 126 +++++++ web/src/components/AccountCard.tsx | 334 +++++++++++++++++ web/src/components/AddAccountForm.tsx | 514 ++++++++++++++++++++++++++ web/src/index.css | 105 ++++++ web/src/main.tsx | 14 + web/tsconfig.json | 20 + web/vite.config.ts | 14 + 21 files changed, 2703 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.console create mode 100644 src/console/account-store.ts create mode 100644 src/console/api.ts create mode 100644 src/console/auth-flow.ts create mode 100644 src/console/index.ts create mode 100644 src/console/instance-manager.ts create mode 100644 src/console/port-check.ts create mode 100644 web/bun.lock create mode 100644 web/index.html create mode 100644 web/package.json create mode 100644 web/src/App.tsx create mode 100644 web/src/api.ts create mode 100644 web/src/components/AccountCard.tsx create mode 100644 web/src/components/AddAccountForm.tsx create mode 100644 web/src/index.css create mode 100644 web/src/main.tsx create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts diff --git a/.dockerignore b/.dockerignore index 84aa78f64..988a539ee 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,3 +11,6 @@ tests/ *.md .eslintcache + +web/node_modules +web/dist diff --git a/Dockerfile.console b/Dockerfile.console new file mode 100644 index 000000000..08fb87cb9 --- /dev/null +++ b/Dockerfile.console @@ -0,0 +1,39 @@ +# Stage 1: Build frontend +FROM node:22-alpine AS web-builder +WORKDIR /app/web + +COPY web/package.json ./ +RUN npm install + +COPY web/ ./ +RUN npm run build + +# Stage 2: Build backend +FROM oven/bun:1.2.19-alpine AS builder +WORKDIR /app + +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +COPY . . + +# Stage 3: Runtime +FROM oven/bun:1.2.19-alpine +WORKDIR /app + +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile --production --ignore-scripts --no-cache + +COPY --from=builder /app/src ./src +COPY --from=builder /app/tsconfig.json ./ +COPY --from=web-builder /app/web/dist ./web/dist + +EXPOSE 3000 + +VOLUME /root/.local/share/copilot-api + +HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ + CMD wget --spider -q http://localhost:3000/api/accounts || exit 1 + +ENTRYPOINT ["bun", "run", "./src/main.ts", "console"] +CMD ["--port", "3000"] diff --git a/bun.lock b/bun.lock index 20e895e7f..9ece87578 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "copilot-api", diff --git a/src/console/account-store.ts b/src/console/account-store.ts new file mode 100644 index 000000000..660fd45c3 --- /dev/null +++ b/src/console/account-store.ts @@ -0,0 +1,78 @@ +import fs from "node:fs/promises" +import path from "node:path" + +import { PATHS } from "~/lib/paths" + +export interface Account { + id: string + name: string + githubToken: string + accountType: string + port: number + enabled: boolean + createdAt: string +} + +export interface AccountStore { + accounts: Array +} + +const STORE_PATH = path.join(PATHS.APP_DIR, "accounts.json") + +async function readStore(): Promise { + try { + const data = await fs.readFile(STORE_PATH) + return JSON.parse(data) as AccountStore + } catch { + return { accounts: [] } + } +} + +async function writeStore(store: AccountStore): Promise { + await fs.writeFile(STORE_PATH, JSON.stringify(store, null, 2)) +} + +export async function getAccounts(): Promise> { + const store = await readStore() + return store.accounts +} + +export async function getAccount(id: string): Promise { + const store = await readStore() + return store.accounts.find((a) => a.id === id) +} + +export async function addAccount( + account: Omit, +): Promise { + const store = await readStore() + const newAccount: Account = { + ...account, + id: crypto.randomUUID(), + createdAt: new Date().toISOString(), + } + store.accounts.push(newAccount) + await writeStore(store) + return newAccount +} + +export async function updateAccount( + id: string, + updates: Partial>, +): Promise { + const store = await readStore() + const index = store.accounts.findIndex((a) => a.id === id) + if (index === -1) return undefined + store.accounts[index] = { ...store.accounts[index], ...updates } + await writeStore(store) + return store.accounts[index] +} + +export async function deleteAccount(id: string): Promise { + const store = await readStore() + const index = store.accounts.findIndex((a) => a.id === id) + if (index === -1) return false + store.accounts.splice(index, 1) + await writeStore(store) + return true +} diff --git a/src/console/api.ts b/src/console/api.ts new file mode 100644 index 000000000..913cd45bb --- /dev/null +++ b/src/console/api.ts @@ -0,0 +1,257 @@ +import { Hono } from "hono" +import { cors } from "hono/cors" +import { z } from "zod" + +import { + getAccounts, + getAccount, + addAccount, + updateAccount, + deleteAccount, +} from "./account-store" +import { startDeviceFlow, getSession, cleanupSession } from "./auth-flow" +import { + startInstance, + stopInstance, + getInstanceStatus, + getInstanceError, + getInstanceUsage, + getInstanceUser, +} from "./instance-manager" +import { checkPortConflict, findAvailablePort } from "./port-check" + +const AddAccountSchema = z.object({ + name: z.string().min(1), + githubToken: z.string().min(1), + accountType: z.string().default("individual"), + port: z.number().int().min(1024).max(65535), + enabled: z.boolean().default(true), +}) + +const UpdateAccountSchema = z.object({ + name: z.string().min(1).optional(), + githubToken: z.string().min(1).optional(), + accountType: z.string().optional(), + port: z.number().int().min(1024).max(65535).optional(), + enabled: z.boolean().optional(), +}) + +const CompleteAuthSchema = z.object({ + sessionId: z.string().min(1), + name: z.string().min(1), + accountType: z.string().default("individual"), + port: z.number().int().min(1024).max(65535), +}) + +function formatZodError(err: z.ZodError): string { + return z.treeifyError(err).children ? + JSON.stringify(z.treeifyError(err)) + : err.message +} + +function portConflictMessage(conflict: { + port: number + conflict: string + accountName?: string +}): string { + return conflict.conflict === "account" ? + `Port ${conflict.port} is already used by account "${conflict.accountName}"` + : `Port ${conflict.port} is already in use by another process` +} + +export const consoleApi = new Hono() + +consoleApi.use(cors()) + +// List all accounts with status +consoleApi.get("/accounts", async (c) => { + const accounts = await getAccounts() + const result = await Promise.all( + accounts.map(async (account) => { + const status = getInstanceStatus(account.id) + const error = getInstanceError(account.id) + let user: { login: string } | null = null + if (status === "running") { + try { + user = await getInstanceUser(account.id) + } catch { + /* ignore */ + } + } + return { ...account, status, error, user } + }), + ) + return c.json(result) +}) + +// Get single account +consoleApi.get("/accounts/:id", async (c) => { + const account = await getAccount(c.req.param("id")) + if (!account) return c.json({ error: "Account not found" }, 404) + const status = getInstanceStatus(account.id) + const error = getInstanceError(account.id) + return c.json({ ...account, status, error }) +}) + +// Add account +consoleApi.post("/accounts", async (c) => { + const body: unknown = await c.req.json() + const parsed = AddAccountSchema.safeParse(body) + if (!parsed.success) { + return c.json({ error: formatZodError(parsed.error) }, 400) + } + + const conflict = await checkPortConflict(parsed.data.port) + if (conflict) { + return c.json({ error: portConflictMessage(conflict) }, 409) + } + + const account = await addAccount(parsed.data) + return c.json(account, 201) +}) + +// Update account +consoleApi.put("/accounts/:id", async (c) => { + const body: unknown = await c.req.json() + const parsed = UpdateAccountSchema.safeParse(body) + if (!parsed.success) { + return c.json({ error: formatZodError(parsed.error) }, 400) + } + + const id = c.req.param("id") + if (parsed.data.port !== undefined) { + const conflict = await checkPortConflict(parsed.data.port, id) + if (conflict) { + return c.json({ error: portConflictMessage(conflict) }, 409) + } + } + + const account = await updateAccount(id, parsed.data) + if (!account) return c.json({ error: "Account not found" }, 404) + return c.json(account) +}) + +// Delete account +consoleApi.delete("/accounts/:id", async (c) => { + const id = c.req.param("id") + await stopInstance(id) + const deleted = await deleteAccount(id) + if (!deleted) return c.json({ error: "Account not found" }, 404) + return c.json({ ok: true }) +}) + +// Start instance +consoleApi.post("/accounts/:id/start", async (c) => { + const account = await getAccount(c.req.param("id")) + if (!account) return c.json({ error: "Account not found" }, 404) + try { + await startInstance(account) + return c.json({ status: "running" }) + } catch (error) { + return c.json({ error: (error as Error).message, status: "error" }, 500) + } +}) + +// Stop instance +consoleApi.post("/accounts/:id/stop", async (c) => { + await stopInstance(c.req.param("id")) + return c.json({ status: "stopped" }) +}) + +// Get usage for account +consoleApi.get("/accounts/:id/usage", async (c) => { + const usage = await getInstanceUsage(c.req.param("id")) + if (!usage) + return c.json({ error: "Instance not running or usage unavailable" }, 404) + return c.json(usage) +}) + +// === Device Code Auth Flow === + +consoleApi.post("/auth/device-code", async (c) => { + try { + const result = await startDeviceFlow() + return c.json(result) + } catch (error) { + return c.json({ error: (error as Error).message }, 500) + } +}) + +consoleApi.get("/auth/poll/:sessionId", (c) => { + const session = getSession(c.req.param("sessionId")) + if (!session) return c.json({ error: "Session not found" }, 404) + return c.json({ + status: session.status, + accessToken: + session.status === "completed" ? session.accessToken : undefined, + error: session.error, + }) +}) + +consoleApi.post("/auth/complete", async (c) => { + const body: unknown = await c.req.json() + const parsed = CompleteAuthSchema.safeParse(body) + if (!parsed.success) { + return c.json({ error: formatZodError(parsed.error) }, 400) + } + + const session = getSession(parsed.data.sessionId) + if (!session) return c.json({ error: "Session not found" }, 404) + if (session.status !== "completed" || !session.accessToken) { + return c.json({ error: "Auth not completed yet" }, 400) + } + + const conflict = await checkPortConflict(parsed.data.port) + if (conflict) { + return c.json({ error: portConflictMessage(conflict) }, 409) + } + + const account = await addAccount({ + name: parsed.data.name, + githubToken: session.accessToken, + accountType: parsed.data.accountType, + port: parsed.data.port, + enabled: true, + }) + + cleanupSession(parsed.data.sessionId) + return c.json(account, 201) +}) + +// === Port Management === + +consoleApi.get("/port/check/:port", async (c) => { + const port = Number.parseInt(c.req.param("port"), 10) + if (Number.isNaN(port) || port < 1024 || port > 65535) { + return c.json({ error: "Invalid port" }, 400) + } + + const excludeId = c.req.query("exclude") ?? undefined + const conflict = await checkPortConflict(port, excludeId) + + if (conflict) { + return c.json({ + available: false, + conflict: conflict.conflict, + accountName: conflict.accountName, + }) + } + + return c.json({ available: true }) +}) + +consoleApi.get("/port/suggest", async (c) => { + const startRaw = c.req.query("start") ?? "4141" + const start = Number.parseInt(startRaw, 10) + const excludeId = c.req.query("exclude") ?? undefined + + try { + const port = await findAvailablePort( + Number.isNaN(start) ? 4141 : start, + excludeId, + ) + return c.json({ port }) + } catch { + return c.json({ error: "No available port found" }, 500) + } +}) diff --git a/src/console/auth-flow.ts b/src/console/auth-flow.ts new file mode 100644 index 000000000..517a5568b --- /dev/null +++ b/src/console/auth-flow.ts @@ -0,0 +1,139 @@ +import consola from "consola" + +import { + GITHUB_APP_SCOPES, + GITHUB_BASE_URL, + GITHUB_CLIENT_ID, + standardHeaders, +} from "~/lib/api-config" +import { sleep } from "~/lib/utils" + +interface AuthSession { + deviceCode: string + userCode: string + verificationUri: string + expiresAt: number + interval: number + status: "pending" | "completed" | "expired" | "error" + accessToken?: string + error?: string +} + +const sessions = new Map() + +export function getSession(sessionId: string): AuthSession | undefined { + return sessions.get(sessionId) +} + +export async function startDeviceFlow(): Promise<{ + sessionId: string + userCode: string + verificationUri: string + expiresIn: number +}> { + const response = await fetch(`${GITHUB_BASE_URL}/login/device/code`, { + method: "POST", + headers: standardHeaders(), + body: JSON.stringify({ + client_id: GITHUB_CLIENT_ID, + scope: GITHUB_APP_SCOPES, + }), + }) + + if (!response.ok) { + throw new Error(`Failed to get device code: ${response.status}`) + } + + const data = (await response.json()) as { + device_code: string + user_code: string + verification_uri: string + expires_in: number + interval: number + } + + const sessionId = crypto.randomUUID() + const session: AuthSession = { + deviceCode: data.device_code, + userCode: data.user_code, + verificationUri: data.verification_uri, + expiresAt: Date.now() + data.expires_in * 1000, + interval: data.interval, + status: "pending", + } + + sessions.set(sessionId, session) + + // Start background polling + void pollForToken(sessionId, session) + + return { + sessionId, + userCode: data.user_code, + verificationUri: data.verification_uri, + expiresIn: data.expires_in, + } +} + +async function pollForToken( + sessionId: string, + session: AuthSession, +): Promise { + const sleepDuration = (session.interval + 1) * 1000 + + while (session.status === "pending") { + if (Date.now() > session.expiresAt) { + session.status = "expired" + session.error = "Device code expired" + consola.warn(`Auth session ${sessionId} expired`) + return + } + + await sleep(sleepDuration) + + try { + const response = await fetch( + `${GITHUB_BASE_URL}/login/oauth/access_token`, + { + method: "POST", + headers: standardHeaders(), + body: JSON.stringify({ + client_id: GITHUB_CLIENT_ID, + device_code: session.deviceCode, + grant_type: "urn:ietf:params:oauth:grant-type:device_code", + }), + }, + ) + + if (!response.ok) { + consola.debug(`Auth poll failed: ${response.status}`) + continue + } + + const json = (await response.json()) as { + access_token?: string + error?: string + } + + if (json.access_token) { + // eslint-disable-next-line require-atomic-updates + session.accessToken = json.access_token + // eslint-disable-next-line require-atomic-updates + session.status = "completed" + consola.success(`Auth session ${sessionId} completed`) + return + } + + // "authorization_pending" or "slow_down" are expected + if (json.error === "slow_down") { + await sleep(5000) + } + } catch (error) { + consola.debug("Auth poll error:", error) + } + } +} + +export function cleanupSession(sessionId: string): void { + sessions.delete(sessionId) +} diff --git a/src/console/index.ts b/src/console/index.ts new file mode 100644 index 000000000..863d7af51 --- /dev/null +++ b/src/console/index.ts @@ -0,0 +1,132 @@ +import { defineCommand } from "citty" +import consola from "consola" +import { Hono } from "hono" +import { cors } from "hono/cors" +import fs from "node:fs/promises" +import path from "node:path" +import { serve, type ServerHandler } from "srvx" + +import { ensurePaths } from "~/lib/paths" + +import { getAccounts } from "./account-store" +import { consoleApi } from "./api" +import { startInstance } from "./instance-manager" + +const MIME_TYPES: Record = { + ".html": "text/html", + ".js": "application/javascript", + ".css": "text/css", + ".json": "application/json", + ".png": "image/png", + ".svg": "image/svg+xml", + ".ico": "image/x-icon", +} + +export const console_ = defineCommand({ + meta: { + name: "console", + description: "Start the multi-account management console", + }, + args: { + port: { + alias: "p", + type: "string", + default: "3000", + description: "Port for the console web UI", + }, + verbose: { + alias: "v", + type: "boolean", + default: false, + description: "Enable verbose logging", + }, + "auto-start": { + type: "boolean", + default: true, + description: "Auto-start enabled accounts on launch", + }, + }, + async run({ args }) { + if (args.verbose) { + consola.level = 5 + } + + await ensurePaths() + + const port = Number.parseInt(args.port, 10) + const app = new Hono() + + app.use(cors()) + + // Mount API routes + app.route("/api", consoleApi) + + // Serve static frontend + const webDistPath = path.resolve( + new URL(".", import.meta.url).pathname, + "../../web/dist", + ) + + let hasWebDist = false + try { + await fs.access(webDistPath) + hasWebDist = true + } catch { + /* no built frontend */ + } + + if (hasWebDist) { + app.get("/*", async (c) => { + const reqPath = c.req.path === "/" ? "/index.html" : c.req.path + const filePath = path.join(webDistPath, reqPath) + + try { + const content = await fs.readFile(filePath) + const ext = path.extname(filePath) + return c.body(content.buffer as ArrayBuffer, { + headers: { + "content-type": MIME_TYPES[ext] ?? "application/octet-stream", + }, + }) + } catch { + // SPA fallback + const indexContent = await fs.readFile( + path.join(webDistPath, "index.html"), + "utf8", + ) + return c.html(indexContent) + } + }) + } else { + app.get("/", (c) => + c.text( + "Console API running. Build the web UI with: cd web && bun install && bun run build", + ), + ) + } + + // Auto-start enabled accounts + if (args["auto-start"]) { + const accounts = await getAccounts() + for (const account of accounts) { + if (account.enabled) { + try { + await startInstance(account) + } catch (error) { + consola.error( + `Failed to auto-start account "${account.name}":`, + error, + ) + } + } + } + } + + serve({ + fetch: app.fetch as ServerHandler, + port, + }) + + consola.box(`🎛️ Console running at http://localhost:${port}`) + }, +}) diff --git a/src/console/instance-manager.ts b/src/console/instance-manager.ts new file mode 100644 index 000000000..a93aab721 --- /dev/null +++ b/src/console/instance-manager.ts @@ -0,0 +1,446 @@ +import type { Context } from "hono" + +import consola from "consola" +import { events } from "fetch-event-stream" +import { Hono } from "hono" +import { cors } from "hono/cors" +import { logger } from "hono/logger" +import { streamSSE } from "hono/streaming" +import { serve, type ServerHandler } from "srvx" + +import type { State } from "~/lib/state" +import type { AnthropicMessagesPayload } from "~/routes/messages/anthropic-types" +import type { + ChatCompletionResponse, + ChatCompletionsPayload, + ChatCompletionChunk, +} from "~/services/copilot/create-chat-completions" +import type { ModelsResponse } from "~/services/copilot/get-models" + +import { + copilotBaseUrl, + copilotHeaders, + GITHUB_API_BASE_URL, + githubHeaders, + standardHeaders, +} from "~/lib/api-config" +import { HTTPError } from "~/lib/error" +import { getTokenCount } from "~/lib/tokenizer" +import { + translateToOpenAI, + translateToAnthropic, +} from "~/routes/messages/non-stream-translation" +import { translateChunkToAnthropicEvents } from "~/routes/messages/stream-translation" +import { getVSCodeVersion } from "~/services/get-vscode-version" + +import type { Account } from "./account-store" + +interface ProxyInstance { + account: Account + state: State + server: ReturnType | null + status: "running" | "stopped" | "error" + error?: string + tokenInterval?: ReturnType +} + +const instances = new Map() + +function createState(account: Account): State { + return { + accountType: account.accountType, + githubToken: account.githubToken, + manualApprove: false, + rateLimitWait: false, + showToken: false, + } +} + +async function fetchCopilotToken( + st: State, +): Promise<{ token: string; refresh_in: number }> { + const response = await fetch( + `${GITHUB_API_BASE_URL}/copilot_internal/v2/token`, + { headers: githubHeaders(st) }, + ) + if (!response.ok) throw new HTTPError("Failed to get Copilot token", response) + return (await response.json()) as { token: string; refresh_in: number } +} + +async function fetchModels(st: State): Promise { + const response = await fetch(`${copilotBaseUrl(st)}/models`, { + headers: copilotHeaders(st), + }) + if (!response.ok) throw new HTTPError("Failed to get models", response) + return (await response.json()) as ModelsResponse +} + +async function setupInstanceToken(instance: ProxyInstance): Promise { + const { token, refresh_in } = await fetchCopilotToken(instance.state) + // eslint-disable-next-line require-atomic-updates + instance.state.copilotToken = token + + const refreshMs = (refresh_in - 60) * 1000 + // eslint-disable-next-line require-atomic-updates + instance.tokenInterval = setInterval(() => { + void (async () => { + try { + const result = await fetchCopilotToken(instance.state) + // eslint-disable-next-line require-atomic-updates + instance.state.copilotToken = result.token + consola.debug(`[${instance.account.name}] Copilot token refreshed`) + } catch (error) { + consola.error( + `[${instance.account.name}] Failed to refresh token:`, + error, + ) + } + })() + }, refreshMs) +} + +export async function startInstance(account: Account): Promise { + const existing = instances.get(account.id) + if (existing?.status === "running") { + consola.warn(`Instance for ${account.name} is already running`) + return + } + + const st = createState(account) + const instance: ProxyInstance = { + account, + state: st, + server: null, + status: "stopped", + } + + try { + st.vsCodeVersion = await getVSCodeVersion() + await setupInstanceToken(instance) + st.models = await fetchModels(st) + + const app = createInstanceServer(instance) + instance.server = serve({ + fetch: app.fetch as ServerHandler, + port: account.port, + }) + instance.status = "running" + instances.set(account.id, instance) + consola.success(`[${account.name}] Proxy started on port ${account.port}`) + } catch (error) { + instance.status = "error" + instance.error = (error as Error).message + instances.set(account.id, instance) + consola.error(`[${account.name}] Failed to start:`, error) + throw error + } +} + +export async function stopInstance(accountId: string): Promise { + const instance = instances.get(accountId) + if (!instance) return + + try { + if (instance.tokenInterval) clearInterval(instance.tokenInterval) + if (instance.server) await instance.server.close() + instance.status = "stopped" + instance.server = null + consola.info(`[${instance.account.name}] Proxy stopped`) + } catch (error) { + consola.error(`[${instance.account.name}] Error stopping:`, error) + } +} + +export function getInstanceStatus(accountId: string): ProxyInstance["status"] { + return instances.get(accountId)?.status ?? "stopped" +} + +export function getInstanceError(accountId: string): string | undefined { + return instances.get(accountId)?.error +} + +export async function getInstanceUsage(accountId: string): Promise { + const instance = instances.get(accountId) + if (!instance || instance.status !== "running") return null + try { + const response = await fetch( + `${GITHUB_API_BASE_URL}/copilot_internal/user`, + { headers: githubHeaders(instance.state) }, + ) + if (!response.ok) return null + return await response.json() + } catch { + return null + } +} + +export async function getInstanceUser( + accountId: string, +): Promise<{ login: string } | null> { + const instance = instances.get(accountId) + if (!instance) return null + try { + const response = await fetch(`${GITHUB_API_BASE_URL}/user`, { + headers: { + authorization: `token ${instance.state.githubToken}`, + ...standardHeaders(), + }, + }) + if (!response.ok) return null + return (await response.json()) as { login: string } + } catch { + return null + } +} + +// === Per-instance server === + +function createInstanceServer(instance: ProxyInstance): Hono { + const app = new Hono() + app.use(logger()) + app.use(cors()) + + const st = instance.state + + app.get("/", (c) => c.text(`Proxy running for ${instance.account.name}`)) + + app.post("/chat/completions", (c) => completionsHandler(c, st)) + app.post("/v1/chat/completions", (c) => completionsHandler(c, st)) + app.get("/models", (c) => modelsHandler(c, st)) + app.get("/v1/models", (c) => modelsHandler(c, st)) + app.post("/embeddings", (c) => embeddingsHandler(c, st)) + app.post("/v1/embeddings", (c) => embeddingsHandler(c, st)) + app.post("/v1/messages", (c) => messagesHandler(c, st)) + app.post("/v1/messages/count_tokens", (c) => countTokensHandler(c, st)) + + app.get("/usage", async (c) => { + try { + const response = await fetch( + `${GITHUB_API_BASE_URL}/copilot_internal/user`, + { headers: githubHeaders(st) }, + ) + if (!response.ok) throw new HTTPError("Failed to get usage", response) + return c.json(await response.json()) + } catch (error) { + return c.json({ error: (error as Error).message }, 500) + } + }) + + app.get("/token", (c) => c.json({ token: st.copilotToken })) + + return app +} + +interface CompletionsPayload { + messages: Array<{ + role: string + content: unknown + }> + model: string + max_tokens?: number + stream?: boolean +} + +function hasVisionContent(messages: CompletionsPayload["messages"]): boolean { + return messages.some( + (x) => + typeof x.content !== "string" + && Array.isArray(x.content) + && (x.content as Array<{ type: string }>).some( + (p) => p.type === "image_url", + ), + ) +} + +function isAgentRequest(messages: CompletionsPayload["messages"]): boolean { + return messages.some((msg) => ["assistant", "tool"].includes(msg.role)) +} + +async function completionsHandler(c: Context, st: State) { + try { + const payload = await c.req.json() + + const headers: Record = { + ...copilotHeaders(st, hasVisionContent(payload.messages)), + "X-Initiator": isAgentRequest(payload.messages) ? "agent" : "user", + } + + if (!payload.max_tokens) { + const model = st.models?.data.find((m) => m.id === payload.model) + if (model) { + payload.max_tokens = model.capabilities.limits.max_output_tokens + } + } + + const response = await fetch(`${copilotBaseUrl(st)}/chat/completions`, { + method: "POST", + headers, + body: JSON.stringify(payload), + }) + + if (!response.ok) { + const text = await response.text() + return c.json( + { error: { message: text, type: "error" } }, + response.status as 400, + ) + } + + if (payload.stream) { + return new Response(response.body, { + headers: { + "content-type": "text/event-stream", + "cache-control": "no-cache", + connection: "keep-alive", + }, + }) + } + + return c.json(await response.json()) + } catch (error) { + return c.json( + { error: { message: (error as Error).message, type: "error" } }, + 500, + ) + } +} + +function modelsHandler(c: Context, st: State) { + const models = + st.models?.data.map((model) => ({ + id: model.id, + object: "model", + type: "model", + created: 0, + created_at: new Date(0).toISOString(), + owned_by: model.vendor, + display_name: model.name, + })) ?? [] + return c.json({ object: "list", data: models, has_more: false }) +} + +async function embeddingsHandler(c: Context, st: State) { + try { + const payload = await c.req.json>() + const response = await fetch(`${copilotBaseUrl(st)}/embeddings`, { + method: "POST", + headers: copilotHeaders(st), + body: JSON.stringify(payload), + }) + if (!response.ok) + throw new HTTPError("Failed to create embeddings", response) + return c.json(await response.json()) + } catch (error) { + return c.json( + { error: { message: (error as Error).message, type: "error" } }, + 500, + ) + } +} + +async function messagesHandler(c: Context, st: State) { + try { + const anthropicPayload = await c.req.json() + const openAIPayload = translateToOpenAI(anthropicPayload) + + if (!openAIPayload.max_tokens) { + const model = st.models?.data.find((m) => m.id === openAIPayload.model) + if (model) { + openAIPayload.max_tokens = model.capabilities.limits.max_output_tokens + } + } + + const enableVision = openAIPayload.messages.some( + (x) => + typeof x.content !== "string" + && Array.isArray(x.content) + && x.content.some((p) => p.type === "image_url"), + ) + + const isAgentCall = openAIPayload.messages.some((msg) => + ["assistant", "tool"].includes(msg.role), + ) + + const headers: Record = { + ...copilotHeaders(st, enableVision), + "X-Initiator": isAgentCall ? "agent" : "user", + } + + const response = await fetch(`${copilotBaseUrl(st)}/chat/completions`, { + method: "POST", + headers, + body: JSON.stringify(openAIPayload), + }) + + if (!response.ok) { + const text = await response.text() + return c.json( + { error: { message: text, type: "error" } }, + response.status as 400, + ) + } + + if (openAIPayload.stream) { + return streamSSE(c, async (stream) => { + const streamState = { + messageStartSent: false, + contentBlockIndex: 0, + contentBlockOpen: false, + toolCalls: {} as Record< + number, + { id: string; name: string; anthropicBlockIndex: number } + >, + } + + const eventStream = events(response) + for await (const rawEvent of eventStream) { + if (rawEvent.data === "[DONE]") break + if (!rawEvent.data) continue + + const chunk = JSON.parse(rawEvent.data) as ChatCompletionChunk + const translated = translateChunkToAnthropicEvents(chunk, streamState) + for (const event of translated) { + await stream.writeSSE({ + event: event.type, + data: JSON.stringify(event), + }) + } + } + }) + } + + const openAIResponse = (await response.json()) as ChatCompletionResponse + return c.json(translateToAnthropic(openAIResponse)) + } catch (error) { + return c.json( + { error: { message: (error as Error).message, type: "error" } }, + 500, + ) + } +} + +async function countTokensHandler(c: Context, st: State) { + try { + const anthropicPayload = await c.req.json() + const openAIPayload: ChatCompletionsPayload = + translateToOpenAI(anthropicPayload) + + const selectedModel = st.models?.data.find( + (m) => m.id === anthropicPayload.model, + ) + if (!selectedModel) return c.json({ input_tokens: 1 }) + + const tokenCount = await getTokenCount(openAIPayload, selectedModel) + let finalTokenCount = tokenCount.input + tokenCount.output + + if (anthropicPayload.model.startsWith("claude")) { + finalTokenCount = Math.round(finalTokenCount * 1.15) + } else if (anthropicPayload.model.startsWith("grok")) { + finalTokenCount = Math.round(finalTokenCount * 1.03) + } + + return c.json({ input_tokens: finalTokenCount }) + } catch { + return c.json({ input_tokens: 1 }) + } +} diff --git a/src/console/port-check.ts b/src/console/port-check.ts new file mode 100644 index 000000000..e7281b5af --- /dev/null +++ b/src/console/port-check.ts @@ -0,0 +1,74 @@ +import { createServer } from "node:net" + +import { getAccounts, type Account } from "./account-store" + +/** + * Check if a port is available by attempting to bind to it. + */ +export function isPortAvailable(port: number): Promise { + return new Promise((resolve) => { + const server = createServer() + server.once("error", () => resolve(false)) + server.once("listening", () => { + server.close(() => resolve(true)) + }) + server.listen(port, "0.0.0.0") + }) +} + +export interface PortConflict { + port: number + conflict: "account" | "system" + accountName?: string +} + +/** + * Check a port against existing accounts and system availability. + * `excludeAccountId` allows skipping the current account when updating. + */ +export async function checkPortConflict( + port: number, + excludeAccountId?: string, +): Promise { + const accounts = await getAccounts() + const conflicting = accounts.find( + (a) => a.port === port && a.id !== excludeAccountId, + ) + + if (conflicting) { + return { port, conflict: "account", accountName: conflicting.name } + } + + const available = await isPortAvailable(port) + if (!available) { + return { port, conflict: "system" } + } + + return null +} + +/** + * Find the next available port starting from a given port. + */ +export async function findAvailablePort( + startPort: number, + excludeAccountId?: string, +): Promise { + const accounts = await getAccounts() + const usedPorts = new Set( + accounts + .filter((a: Account) => a.id !== excludeAccountId) + .map((a: Account) => a.port), + ) + + let port = startPort + while (port <= 65535) { + if (!usedPorts.has(port)) { + const available = await isPortAvailable(port) + if (available) return port + } + port++ + } + + throw new Error("No available port found") +} diff --git a/src/main.ts b/src/main.ts index 4f6ca784b..0dfc71e75 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import { defineCommand, runMain } from "citty" import { auth } from "./auth" import { checkUsage } from "./check-usage" +import { console_ } from "./console/index" import { debug } from "./debug" import { start } from "./start" @@ -13,7 +14,13 @@ const main = defineCommand({ description: "A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools.", }, - subCommands: { auth, start, "check-usage": checkUsage, debug }, + subCommands: { + auth, + start, + "check-usage": checkUsage, + debug, + console: console_, + }, }) await runMain(main) diff --git a/web/bun.lock b/web/bun.lock new file mode 100644 index 000000000..ed4a70380 --- /dev/null +++ b/web/bun.lock @@ -0,0 +1,257 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "copilot-api-console", + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0", + }, + "devDependencies": { + "@types/react": "^19.1.6", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.5.2", + "typescript": "^5.9.3", + "vite": "^6.3.5", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], + + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], + + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], + + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], + + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], + + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], + + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + + "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], + + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + } +} diff --git a/web/index.html b/web/index.html new file mode 100644 index 000000000..d0c45037a --- /dev/null +++ b/web/index.html @@ -0,0 +1,12 @@ + + + + + + Copilot API Console + + +
+ + + diff --git a/web/package.json b/web/package.json new file mode 100644 index 000000000..60c8bfff6 --- /dev/null +++ b/web/package.json @@ -0,0 +1,22 @@ +{ + "name": "copilot-api-console", + "private": true, + "description": "Web console for managing multiple GitHub Copilot proxy accounts", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/react": "^19.1.6", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.5.2", + "typescript": "^5.9.3", + "vite": "^6.3.5" + } +} diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 000000000..aaf42ddec --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,108 @@ +import { useState, useEffect, useCallback } from "react" + +import { api, type Account } from "./api" +import { AccountCard } from "./components/AccountCard" +import { AddAccountForm } from "./components/AddAccountForm" + +function AccountList({ + accounts, + onRefresh, +}: { + accounts: Array + onRefresh: () => Promise +}) { + if (accounts.length === 0) { + return ( +
+

No accounts configured

+

Add a GitHub account to get started

+
+ ) + } + + return ( +
+ {accounts.map((account) => ( + + ))} +
+ ) +} + +export function App() { + const [accounts, setAccounts] = useState>([]) + const [showForm, setShowForm] = useState(false) + const [loading, setLoading] = useState(true) + + const refresh = useCallback(async () => { + try { + const data = await api.getAccounts() + setAccounts(data) + } catch (err) { + console.error("Failed to fetch accounts:", err) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + void refresh() + const interval = setInterval(() => void refresh(), 5000) + return () => clearInterval(interval) + }, [refresh]) + + const handleAdd = async () => { + setShowForm(false) + await refresh() + } + + return ( +
+
+
+

Copilot API Console

+

+ Manage multiple GitHub Copilot proxy accounts +

+
+ +
+ + {showForm && ( + setShowForm(false)} + /> + )} + + {loading ? +

+ Loading... +

+ : } +
+ ) +} diff --git a/web/src/api.ts b/web/src/api.ts new file mode 100644 index 000000000..4a5ba5c85 --- /dev/null +++ b/web/src/api.ts @@ -0,0 +1,126 @@ +const BASE = "/api" + +export interface Account { + id: string + name: string + githubToken: string + accountType: string + port: number + enabled: boolean + createdAt: string + status?: "running" | "stopped" | "error" + error?: string + user?: { login: string } | null +} + +export interface UsageData { + copilot_plan: string + quota_reset_date: string + quota_snapshots: { + premium_interactions: QuotaDetail + chat: QuotaDetail + completions: QuotaDetail + } +} + +interface QuotaDetail { + entitlement: number + remaining: number + percent_remaining: number +} + +export interface DeviceCodeResponse { + sessionId: string + userCode: string + verificationUri: string + expiresIn: number +} + +export interface AuthPollResponse { + status: "pending" | "completed" | "expired" | "error" + accessToken?: string + error?: string +} + +interface ErrorBody { + error?: string +} + +async function request(path: string, options?: RequestInit): Promise { + const res = await fetch(`${BASE}${path}`, { + ...options, + headers: { + "Content-Type": "application/json", + ...(options?.headers as Record), + }, + }) + if (!res.ok) { + const body = (await res.json().catch(() => ({}))) as ErrorBody + throw new Error(body.error ?? `HTTP ${res.status}`) + } + return res.json() as Promise +} + +export const api = { + getAccounts: () => request>("/accounts"), + + addAccount: (data: { + name: string + githubToken: string + accountType: string + port: number + }) => + request("/accounts", { + method: "POST", + body: JSON.stringify(data), + }), + + updateAccount: (id: string, data: Partial) => + request(`/accounts/${id}`, { + method: "PUT", + body: JSON.stringify(data), + }), + + deleteAccount: (id: string) => + request<{ ok: boolean }>(`/accounts/${id}`, { method: "DELETE" }), + + startInstance: (id: string) => + request<{ status: string }>(`/accounts/${id}/start`, { method: "POST" }), + + stopInstance: (id: string) => + request<{ status: string }>(`/accounts/${id}/stop`, { method: "POST" }), + + getUsage: (id: string) => request(`/accounts/${id}/usage`), + + startDeviceCode: () => + request("/auth/device-code", { method: "POST" }), + + pollAuth: (sessionId: string) => + request(`/auth/poll/${sessionId}`), + + completeAuth: (data: { + sessionId: string + name: string + accountType: string + port: number + }) => + request("/auth/complete", { + method: "POST", + body: JSON.stringify(data), + }), + + checkPort: (port: number, excludeId?: string) => + request<{ + available: boolean + conflict?: string + accountName?: string + }>(`/port/check/${port}${excludeId ? `?exclude=${excludeId}` : ""}`), + + suggestPort: (start?: number, excludeId?: string) => { + const params = new URLSearchParams() + if (start !== undefined) params.set("start", String(start)) + if (excludeId) params.set("exclude", excludeId) + const qs = params.toString() + return request<{ port: number }>(`/port/suggest${qs ? `?${qs}` : ""}`) + }, +} diff --git a/web/src/components/AccountCard.tsx b/web/src/components/AccountCard.tsx new file mode 100644 index 000000000..7f7c1ddd9 --- /dev/null +++ b/web/src/components/AccountCard.tsx @@ -0,0 +1,334 @@ +import { useState } from "react" + +import { api, type Account, type UsageData } from "../api" + +function StatusBadge({ status }: { status: string }) { + const colorMap: Record = { + running: "var(--green)", + stopped: "var(--text-muted)", + error: "var(--red)", + } + const color = colorMap[status] ?? "var(--text-muted)" + return ( + + + {status} + + ) +} + +function QuotaBar({ + label, + used, + total, +}: { + label: string + used: number + total: number +}) { + const pct = total > 0 ? (used / total) * 100 : 0 + let color = "var(--green)" + if (pct > 90) color = "var(--red)" + else if (pct > 70) color = "var(--yellow)" + + return ( +
+
+ {label} + + {used} / {total} ({pct.toFixed(1)}%) + +
+
+
+
+
+ ) +} + +function UsagePanel({ usage }: { usage: UsageData }) { + const q = usage.quota_snapshots + return ( +
+
+ Plan: {usage.copilot_plan} · Resets: {usage.quota_reset_date} +
+ + + +
+ ) +} + +function EndpointsPanel({ port }: { port: number }) { + return ( +
+
+ Endpoints +
+
OpenAI: http://localhost:{port}/v1/chat/completions
+
Anthropic: http://localhost:{port}/v1/messages
+
Models: http://localhost:{port}/v1/models
+
+ ) +} + +function getUsageLabel(loading: boolean, visible: boolean): string { + if (loading) return "..." + if (visible) return "Hide Usage" + return "Usage" +} + +interface Props { + account: Account + onRefresh: () => Promise +} + +function AccountActions({ + account, + status, + onRefresh, +}: { + account: Account + status: string + onRefresh: () => Promise +}) { + const [actionLoading, setActionLoading] = useState(false) + const [confirmDelete, setConfirmDelete] = useState(false) + + const handleAction = async (action: () => Promise) => { + setActionLoading(true) + try { + await action() + await onRefresh() + } catch (err) { + console.error("Action failed:", err) + } finally { + setActionLoading(false) + } + } + + const handleDelete = async () => { + if (!confirmDelete) { + setConfirmDelete(true) + setTimeout(() => setConfirmDelete(false), 3000) + return + } + setActionLoading(true) + try { + await api.deleteAccount(account.id) + await onRefresh() + } catch (err) { + console.error("Delete failed:", err) + } finally { + setActionLoading(false) + setConfirmDelete(false) + } + } + + return ( +
+ {status === "running" ? + + : + } + +
+ ) +} + +function UsageSection({ accountId }: { accountId: string }) { + const [usage, setUsage] = useState(null) + const [usageLoading, setUsageLoading] = useState(false) + const [showUsage, setShowUsage] = useState(false) + + const handleToggleUsage = async () => { + if (showUsage) { + setShowUsage(false) + return + } + setUsageLoading(true) + try { + const data = await api.getUsage(accountId) + setUsage(data) + setShowUsage(true) + } catch { + setUsage(null) + setShowUsage(true) + } finally { + setUsageLoading(false) + } + } + + return ( + <> + + {showUsage + && (usage ? + + :
+ Usage data unavailable. Make sure the instance is running. +
)} + + ) +} + +export function AccountCard({ account, onRefresh }: Props) { + const status = account.status ?? "stopped" + + return ( +
+
+
+
+ + {account.name} + + +
+
+ {account.user?.login ? `@${account.user.login} · ` : ""} + Port {account.port} · {account.accountType} +
+ {account.error && ( +
+ Error: {account.error} +
+ )} +
+ + +
+ + {status === "running" && } + {status === "running" && } +
+ ) +} diff --git a/web/src/components/AddAccountForm.tsx b/web/src/components/AddAccountForm.tsx new file mode 100644 index 000000000..324c0b04d --- /dev/null +++ b/web/src/components/AddAccountForm.tsx @@ -0,0 +1,514 @@ +import { useState, useEffect, useRef, useCallback } from "react" + +import { api } from "../api" + +interface Props { + onComplete: () => Promise + onCancel: () => void +} + +type Step = "config" | "authorize" | "done" +type PortStatus = "idle" | "checking" | "ok" | "conflict" + +function PortIndicator({ status }: { status: PortStatus }) { + if (status === "idle") return null + const colorMap: Record = { + ok: "var(--green)", + conflict: "var(--red)", + checking: "var(--yellow)", + } + return ( + + ) +} + +function DeviceCodeDisplay({ + userCode, + verificationUri, +}: { + userCode: string + verificationUri: string +}) { + return ( +
+

+ Enter this code on GitHub: +

+
void navigator.clipboard.writeText(userCode)} + style={{ + display: "inline-block", + padding: "12px 24px", + background: "var(--bg)", + border: "2px solid var(--accent)", + borderRadius: "var(--radius)", + fontSize: 28, + fontWeight: 700, + fontFamily: "monospace", + letterSpacing: 4, + cursor: "pointer", + userSelect: "all", + marginBottom: 8, + }} + title="Click to copy" + > + {userCode} +
+

+ Click the code to copy +

+ + Open GitHub + +
+ ) +} + +function AuthorizeStep({ + userCode, + verificationUri, + authStatus, + error, + onCancel, +}: { + userCode: string + verificationUri: string + authStatus: string + error: string + onCancel: () => void +}) { + return ( +
+

+ GitHub Authorization +

+ +

+ + {authStatus} +

+ {error && ( +
+ {error} +
+ )} +
+ +
+
+ ) +} + +function usePortCheck() { + const [portStatus, setPortStatus] = useState("idle") + const [portMessage, setPortMessage] = useState("") + const timerRef = useRef | null>(null) + + const check = useCallback((value: string) => { + if (timerRef.current) clearTimeout(timerRef.current) + const portNum = Number.parseInt(value, 10) + if (Number.isNaN(portNum) || portNum < 1024 || portNum > 65535) { + setPortStatus("conflict") + setPortMessage("Port must be 1024–65535") + return + } + setPortStatus("checking") + setPortMessage("") + timerRef.current = setTimeout(() => { + void (async () => { + try { + const result = await api.checkPort(portNum) + if (result.available) { + setPortStatus("ok") + setPortMessage("") + } else { + setPortStatus("conflict") + setPortMessage( + result.conflict === "account" ? + `Used by "${result.accountName}"` + : "Occupied by another process", + ) + } + } catch { + setPortStatus("idle") + } + })() + }, 400) + }, []) + + return { portStatus, portMessage, check, setPortStatus, setPortMessage } +} + +function getPortBorderColor(status: PortStatus): string | undefined { + if (status === "conflict") return "var(--red)" + if (status === "ok") return "var(--green)" + return undefined +} + +function PortField({ + port, + portStatus, + portMessage, + onPortChange, + onAutoPort, +}: { + port: string + portStatus: PortStatus + portMessage: string + onPortChange: (v: string) => void + onAutoPort: () => void +}) { + return ( +
+ +
+ onPortChange(e.target.value)} + placeholder="4141" + style={{ borderColor: getPortBorderColor(portStatus) }} + /> + +
+ {portMessage && ( +
+ {portMessage} +
+ )} +
+ ) +} + +interface ConfigFormProps { + onSubmit: (e: React.SyntheticEvent) => void + onCancel: () => void + loading: boolean + error: string + portStatus: PortStatus + portMessage: string + name: string + setName: (v: string) => void + accountType: string + setAccountType: (v: string) => void + port: string + onPortChange: (v: string) => void + onAutoPort: () => void +} + +function ConfigForm(props: ConfigFormProps) { + const isDisabled = + props.loading + || props.portStatus === "conflict" + || props.portStatus === "checking" + + return ( +
+

+ Add Account +

+
+
+ + props.setName(e.target.value)} + placeholder="e.g. Personal" + /> +
+ +
+
+ + +
+ {props.error && ( +
+ {props.error} +
+ )} +
+ + +
+
+ ) +} + +function useAuthFlow(onComplete: () => Promise) { + const [step, setStep] = useState("config") + const [userCode, setUserCode] = useState("") + const [verificationUri, setVerificationUri] = useState("") + const [authStatus, setAuthStatus] = useState("") + const [loading, setLoading] = useState(false) + const [error, setError] = useState("") + const pollRef = useRef | null>(null) + + const cleanup = useCallback(() => { + if (pollRef.current) { + clearInterval(pollRef.current) + pollRef.current = null + } + }, []) + + useEffect(() => cleanup, [cleanup]) + + const startAuth = async ( + name: string, + accountType: string, + portNum: number, + ) => { + setError("") + setLoading(true) + try { + const result = await api.startDeviceCode() + setUserCode(result.userCode) + setVerificationUri(result.verificationUri) + setStep("authorize") + setAuthStatus("Waiting for authorization...") + + pollRef.current = setInterval(() => { + void (async () => { + try { + const poll = await api.pollAuth(result.sessionId) + if (poll.status === "completed") { + cleanup() + setAuthStatus("Authorized! Creating account...") + await api.completeAuth({ + sessionId: result.sessionId, + name, + accountType, + port: portNum, + }) + setStep("done") + await onComplete() + } else if (poll.status === "expired" || poll.status === "error") { + cleanup() + setAuthStatus("") + setError(poll.error ?? "Authorization failed or expired") + } + } catch { + // poll error, keep trying + } + })() + }, 3000) + } catch (err) { + setError((err as Error).message) + } finally { + setLoading(false) + } + } + + return { + step, + userCode, + verificationUri, + authStatus, + loading, + error, + setError, + cleanup, + startAuth, + } +} + +export function AddAccountForm({ onComplete, onCancel }: Props) { + const [name, setName] = useState("") + const [accountType, setAccountType] = useState("individual") + const [port, setPort] = useState("") + const { portStatus, portMessage, check, setPortStatus, setPortMessage } = + usePortCheck() + const auth = useAuthFlow(onComplete) + + useEffect(() => { + void api.suggestPort(4141).then((res) => { + setPort(String(res.port)) + setPortStatus("ok") + setPortMessage("") + }) + }, [setPortStatus, setPortMessage]) + + const handlePortChange = (value: string) => { + setPort(value) + check(value) + } + + const handleAutoPort = () => { + void (async () => { + try { + const res = await api.suggestPort(Number.parseInt(port, 10) || 4141) + setPort(String(res.port)) + setPortStatus("ok") + setPortMessage("") + } catch { + auth.setError("Failed to find available port") + } + })() + } + + const handleSubmit = (e: React.SyntheticEvent) => { + e.preventDefault() + if (!name.trim()) { + auth.setError("Account name is required") + return + } + const portNum = Number.parseInt(port, 10) + if (Number.isNaN(portNum) || portNum < 1024 || portNum > 65535) { + auth.setError("Port must be between 1024 and 65535") + return + } + if (portStatus === "conflict") { + auth.setError("Please resolve the port conflict first") + return + } + void auth.startAuth(name.trim(), accountType, portNum) + } + + if (auth.step === "done") return null + + const wrapperStyle = { + background: "var(--bg-card)", + border: "1px solid var(--border)", + borderRadius: "var(--radius)", + padding: 20, + marginBottom: 16, + } + + return ( +
+ {auth.step === "config" && ( + + )} + {auth.step === "authorize" && ( + { + auth.cleanup() + onCancel() + }} + /> + )} + +
+ ) +} diff --git a/web/src/index.css b/web/src/index.css new file mode 100644 index 000000000..e720b8f9b --- /dev/null +++ b/web/src/index.css @@ -0,0 +1,105 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --bg: #0d1117; + --bg-card: #161b22; + --bg-input: #0d1117; + --border: #30363d; + --text: #e6edf3; + --text-muted: #8b949e; + --accent: #58a6ff; + --accent-hover: #79c0ff; + --green: #3fb950; + --red: #f85149; + --yellow: #d29922; + --radius: 8px; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, + sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.5; +} + +button { + cursor: pointer; + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 6px 16px; + font-size: 14px; + background: var(--bg-card); + color: var(--text); + transition: background 0.15s; +} + +button:hover { + background: var(--border); +} + +button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +button.primary { + background: var(--accent); + color: #fff; + border-color: var(--accent); +} + +button.primary:hover { + background: var(--accent-hover); +} + +button.danger { + color: var(--red); + border-color: var(--red); +} + +button.danger:hover { + background: rgba(248, 81, 73, 0.15); +} + +input, +select { + background: var(--bg-input); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 8px 12px; + color: var(--text); + font-size: 14px; + width: 100%; +} + +input:focus, +select:focus { + outline: none; + border-color: var(--accent); +} + +label { + display: block; + font-size: 13px; + color: var(--text-muted); + margin-bottom: 4px; +} + +/* Hide number input spinners */ +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type="number"] { + -moz-appearance: textfield; + appearance: textfield; +} diff --git a/web/src/main.tsx b/web/src/main.tsx new file mode 100644 index 000000000..c047c907c --- /dev/null +++ b/web/src/main.tsx @@ -0,0 +1,14 @@ +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" + +import { App } from "./App" +import "./index.css" + +const root = document.querySelector("#root") +if (root) { + createRoot(root).render( + + + , + ) +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 000000000..109f0ac28 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 000000000..d9b9c19d8 --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,14 @@ +import react from "@vitejs/plugin-react" +import { defineConfig } from "vite" + +export default defineConfig({ + plugins: [react()], + server: { + proxy: { + "/api": "http://localhost:3000", + }, + }, + build: { + outDir: "dist", + }, +}) From edee63fa41ae412c1364a3412500f3765bece0f6 Mon Sep 17 00:00:00 2001 From: haruka <1628615876@qq.com> Date: Fri, 13 Feb 2026 19:09:42 +0800 Subject: [PATCH 02/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=89=B4=E6=9D=83?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 10 + .idea/codeStyles/Project.xml | 59 ++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/copilot-api.iml | 8 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + Dockerfile.console | 6 +- eslint.config.js | 1 + src/console/account-store.ts | 30 ++- src/console/api.ts | 165 +++++------- src/console/index.ts | 235 +++++++++++----- src/console/instance-manager.ts | 112 +++----- src/console/port-check.ts | 74 ----- web/src/App.tsx | 72 ++++- web/src/api.ts | 65 ++--- web/src/components/AccountCard.tsx | 213 +++++++++++---- web/src/components/AddAccountForm.tsx | 267 +++---------------- web/vite.config.ts | 5 + 19 files changed, 698 insertions(+), 649 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/copilot-api.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml delete mode 100644 src/console/port-check.ts diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..b6b1ecf10 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 已忽略包含查询文件的默认文件夹 +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..238b56b02 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copilot-api.iml b/.idea/copilot-api.iml new file mode 100644 index 000000000..c956989b2 --- /dev/null +++ b/.idea/copilot-api.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 000000000..03d9549ea --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..b5e6487a4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile.console b/Dockerfile.console index 08fb87cb9..858a8f7b7 100644 --- a/Dockerfile.console +++ b/Dockerfile.console @@ -28,12 +28,12 @@ COPY --from=builder /app/src ./src COPY --from=builder /app/tsconfig.json ./ COPY --from=web-builder /app/web/dist ./web/dist -EXPOSE 3000 +EXPOSE 3000 4141 VOLUME /root/.local/share/copilot-api HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ - CMD wget --spider -q http://localhost:3000/api/accounts || exit 1 + CMD wget --spider -q http://localhost:3000/api/auth/check || exit 1 ENTRYPOINT ["bun", "run", "./src/main.ts", "console"] -CMD ["--port", "3000"] +CMD ["--web-port", "3000", "--proxy-port", "4141"] diff --git a/eslint.config.js b/eslint.config.js index c9f79bea5..d4aa43065 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,4 +4,5 @@ export default config({ prettier: { plugins: ["prettier-plugin-packagejson"], }, + ignores: ["web/**"], }) diff --git a/src/console/account-store.ts b/src/console/account-store.ts index 660fd45c3..35b55e360 100644 --- a/src/console/account-store.ts +++ b/src/console/account-store.ts @@ -1,3 +1,4 @@ +import crypto from "node:crypto" import fs from "node:fs/promises" import path from "node:path" @@ -8,7 +9,7 @@ export interface Account { name: string githubToken: string accountType: string - port: number + apiKey: string enabled: boolean createdAt: string } @@ -19,6 +20,10 @@ export interface AccountStore { const STORE_PATH = path.join(PATHS.APP_DIR, "accounts.json") +function generateApiKey(): string { + return `cpa-${crypto.randomBytes(16).toString("hex")}` +} + async function readStore(): Promise { try { const data = await fs.readFile(STORE_PATH) @@ -42,13 +47,21 @@ export async function getAccount(id: string): Promise { return store.accounts.find((a) => a.id === id) } +export async function getAccountByApiKey( + apiKey: string, +): Promise { + const store = await readStore() + return store.accounts.find((a) => a.apiKey === apiKey) +} + export async function addAccount( - account: Omit, + account: Omit, ): Promise { const store = await readStore() const newAccount: Account = { ...account, id: crypto.randomUUID(), + apiKey: generateApiKey(), createdAt: new Date().toISOString(), } store.accounts.push(newAccount) @@ -58,7 +71,7 @@ export async function addAccount( export async function updateAccount( id: string, - updates: Partial>, + updates: Partial>, ): Promise { const store = await readStore() const index = store.accounts.findIndex((a) => a.id === id) @@ -76,3 +89,14 @@ export async function deleteAccount(id: string): Promise { await writeStore(store) return true } + +export async function regenerateApiKey( + id: string, +): Promise { + const store = await readStore() + const index = store.accounts.findIndex((a) => a.id === id) + if (index === -1) return undefined + store.accounts[index] = { ...store.accounts[index], apiKey: generateApiKey() } + await writeStore(store) + return store.accounts[index] +} diff --git a/src/console/api.ts b/src/console/api.ts index 913cd45bb..bea4775ac 100644 --- a/src/console/api.ts +++ b/src/console/api.ts @@ -3,45 +3,33 @@ import { cors } from "hono/cors" import { z } from "zod" import { - getAccounts, - getAccount, addAccount, - updateAccount, deleteAccount, + getAccount, + getAccounts, + regenerateApiKey, + updateAccount, } from "./account-store" -import { startDeviceFlow, getSession, cleanupSession } from "./auth-flow" +import { cleanupSession, getSession, startDeviceFlow } from "./auth-flow" import { - startInstance, - stopInstance, - getInstanceStatus, getInstanceError, + getInstanceStatus, getInstanceUsage, getInstanceUser, + startInstance, + stopInstance, } from "./instance-manager" -import { checkPortConflict, findAvailablePort } from "./port-check" -const AddAccountSchema = z.object({ - name: z.string().min(1), - githubToken: z.string().min(1), - accountType: z.string().default("individual"), - port: z.number().int().min(1024).max(65535), - enabled: z.boolean().default(true), -}) +let adminKey = "" -const UpdateAccountSchema = z.object({ - name: z.string().min(1).optional(), - githubToken: z.string().min(1).optional(), - accountType: z.string().optional(), - port: z.number().int().min(1024).max(65535).optional(), - enabled: z.boolean().optional(), -}) +export function setAdminKey(key: string): void { + adminKey = key +} +let proxyPort = 4141 -const CompleteAuthSchema = z.object({ - sessionId: z.string().min(1), - name: z.string().min(1), - accountType: z.string().default("individual"), - port: z.number().int().min(1024).max(65535), -}) +export function setProxyPort(port: number): void { + proxyPort = port +} function formatZodError(err: z.ZodError): string { return z.treeifyError(err).children ? @@ -49,20 +37,27 @@ function formatZodError(err: z.ZodError): string { : err.message } -function portConflictMessage(conflict: { - port: number - conflict: string - accountName?: string -}): string { - return conflict.conflict === "account" ? - `Port ${conflict.port} is already used by account "${conflict.accountName}"` - : `Port ${conflict.port} is already in use by another process` -} - export const consoleApi = new Hono() consoleApi.use(cors()) +// Public config (no auth required) +consoleApi.get("/config", (c) => c.json({ proxyPort })) + +// Admin auth middleware +consoleApi.use("/*", async (c, next) => { + if (!adminKey) return next() + const auth = c.req.header("authorization") + const token = auth?.replace("Bearer ", "") + if (token !== adminKey) { + return c.json({ error: "Unauthorized" }, 401) + } + return next() +}) + +// Auth check endpoint (for frontend login) +consoleApi.get("/auth/check", (c) => c.json({ ok: true })) + // List all accounts with status consoleApi.get("/accounts", async (c) => { const accounts = await getAccounts() @@ -93,6 +88,13 @@ consoleApi.get("/accounts/:id", async (c) => { return c.json({ ...account, status, error }) }) +const AddAccountSchema = z.object({ + name: z.string().min(1), + githubToken: z.string().min(1), + accountType: z.string().default("individual"), + enabled: z.boolean().default(true), +}) + // Add account consoleApi.post("/accounts", async (c) => { const body: unknown = await c.req.json() @@ -100,16 +102,17 @@ consoleApi.post("/accounts", async (c) => { if (!parsed.success) { return c.json({ error: formatZodError(parsed.error) }, 400) } - - const conflict = await checkPortConflict(parsed.data.port) - if (conflict) { - return c.json({ error: portConflictMessage(conflict) }, 409) - } - const account = await addAccount(parsed.data) return c.json(account, 201) }) +const UpdateAccountSchema = z.object({ + name: z.string().min(1).optional(), + githubToken: z.string().min(1).optional(), + accountType: z.string().optional(), + enabled: z.boolean().optional(), +}) + // Update account consoleApi.put("/accounts/:id", async (c) => { const body: unknown = await c.req.json() @@ -117,16 +120,7 @@ consoleApi.put("/accounts/:id", async (c) => { if (!parsed.success) { return c.json({ error: formatZodError(parsed.error) }, 400) } - - const id = c.req.param("id") - if (parsed.data.port !== undefined) { - const conflict = await checkPortConflict(parsed.data.port, id) - if (conflict) { - return c.json({ error: portConflictMessage(conflict) }, 409) - } - } - - const account = await updateAccount(id, parsed.data) + const account = await updateAccount(c.req.param("id"), parsed.data) if (!account) return c.json({ error: "Account not found" }, 404) return c.json(account) }) @@ -134,12 +128,19 @@ consoleApi.put("/accounts/:id", async (c) => { // Delete account consoleApi.delete("/accounts/:id", async (c) => { const id = c.req.param("id") - await stopInstance(id) + stopInstance(id) const deleted = await deleteAccount(id) if (!deleted) return c.json({ error: "Account not found" }, 404) return c.json({ ok: true }) }) +// Regenerate API key +consoleApi.post("/accounts/:id/regenerate-key", async (c) => { + const account = await regenerateApiKey(c.req.param("id")) + if (!account) return c.json({ error: "Account not found" }, 404) + return c.json(account) +}) + // Start instance consoleApi.post("/accounts/:id/start", async (c) => { const account = await getAccount(c.req.param("id")) @@ -153,8 +154,8 @@ consoleApi.post("/accounts/:id/start", async (c) => { }) // Stop instance -consoleApi.post("/accounts/:id/stop", async (c) => { - await stopInstance(c.req.param("id")) +consoleApi.post("/accounts/:id/stop", (c) => { + stopInstance(c.req.param("id")) return c.json({ status: "stopped" }) }) @@ -188,6 +189,12 @@ consoleApi.get("/auth/poll/:sessionId", (c) => { }) }) +const CompleteAuthSchema = z.object({ + sessionId: z.string().min(1), + name: z.string().min(1), + accountType: z.string().default("individual"), +}) + consoleApi.post("/auth/complete", async (c) => { const body: unknown = await c.req.json() const parsed = CompleteAuthSchema.safeParse(body) @@ -201,57 +208,13 @@ consoleApi.post("/auth/complete", async (c) => { return c.json({ error: "Auth not completed yet" }, 400) } - const conflict = await checkPortConflict(parsed.data.port) - if (conflict) { - return c.json({ error: portConflictMessage(conflict) }, 409) - } - const account = await addAccount({ name: parsed.data.name, githubToken: session.accessToken, accountType: parsed.data.accountType, - port: parsed.data.port, enabled: true, }) cleanupSession(parsed.data.sessionId) return c.json(account, 201) }) - -// === Port Management === - -consoleApi.get("/port/check/:port", async (c) => { - const port = Number.parseInt(c.req.param("port"), 10) - if (Number.isNaN(port) || port < 1024 || port > 65535) { - return c.json({ error: "Invalid port" }, 400) - } - - const excludeId = c.req.query("exclude") ?? undefined - const conflict = await checkPortConflict(port, excludeId) - - if (conflict) { - return c.json({ - available: false, - conflict: conflict.conflict, - accountName: conflict.accountName, - }) - } - - return c.json({ available: true }) -}) - -consoleApi.get("/port/suggest", async (c) => { - const startRaw = c.req.query("start") ?? "4141" - const start = Number.parseInt(startRaw, 10) - const excludeId = c.req.query("exclude") ?? undefined - - try { - const port = await findAvailablePort( - Number.isNaN(start) ? 4141 : start, - excludeId, - ) - return c.json({ port }) - } catch { - return c.json({ error: "No available port found" }, 500) - } -}) diff --git a/src/console/index.ts b/src/console/index.ts index 863d7af51..b55fb242e 100644 --- a/src/console/index.ts +++ b/src/console/index.ts @@ -2,15 +2,25 @@ import { defineCommand } from "citty" import consola from "consola" import { Hono } from "hono" import { cors } from "hono/cors" +import { logger } from "hono/logger" +import crypto from "node:crypto" import fs from "node:fs/promises" import path from "node:path" import { serve, type ServerHandler } from "srvx" import { ensurePaths } from "~/lib/paths" -import { getAccounts } from "./account-store" -import { consoleApi } from "./api" -import { startInstance } from "./instance-manager" +import { getAccountByApiKey, getAccounts } from "./account-store" +import { consoleApi, setAdminKey, setProxyPort } from "./api" +import { + completionsHandler, + countTokensHandler, + embeddingsHandler, + getInstanceState, + messagesHandler, + modelsHandler, + startInstance, +} from "./instance-manager" const MIME_TYPES: Record = { ".html": "text/html", @@ -28,11 +38,21 @@ export const console_ = defineCommand({ description: "Start the multi-account management console", }, args: { - port: { - alias: "p", + "web-port": { + alias: "w", type: "string", default: "3000", - description: "Port for the console web UI", + description: "Port for the web management console", + }, + "proxy-port": { + alias: "p", + type: "string", + default: "4141", + description: "Port for the proxy API endpoints", + }, + "admin-key": { + type: "string", + description: "Admin key for console access (auto-generated if not set)", }, verbose: { alias: "v", @@ -46,6 +66,7 @@ export const console_ = defineCommand({ description: "Auto-start enabled accounts on launch", }, }, + async run({ args }) { if (args.verbose) { consola.level = 5 @@ -53,80 +74,150 @@ export const console_ = defineCommand({ await ensurePaths() - const port = Number.parseInt(args.port, 10) - const app = new Hono() + const webPort = Number.parseInt(args["web-port"], 10) + const proxyPort = Number.parseInt(args["proxy-port"], 10) + const adminKeyArg = args["admin-key"] as string | undefined + const generatedAdminKey = + adminKeyArg ?? `admin-${crypto.randomBytes(8).toString("hex")}` + setAdminKey(generatedAdminKey) + setProxyPort(proxyPort) + + // === Web console server === + const webApp = new Hono() + webApp.use(cors()) + webApp.use(logger()) + webApp.route("/api", consoleApi) + await mountStaticFiles(webApp) - app.use(cors()) + // === Proxy server === + const proxyApp = new Hono() + proxyApp.use(cors()) + mountProxyRoutes(proxyApp) - // Mount API routes - app.route("/api", consoleApi) + // Auto-start enabled accounts + if (args["auto-start"]) { + await autoStartAccounts() + } + + serve({ fetch: webApp.fetch as ServerHandler, port: webPort }) + serve({ fetch: proxyApp.fetch as ServerHandler, port: proxyPort }) - // Serve static frontend - const webDistPath = path.resolve( - new URL(".", import.meta.url).pathname, - "../../web/dist", + consola.box( + [ + `🎛️ Console: http://localhost:${webPort}`, + `🔑 Admin Key: ${generatedAdminKey}`, + "", + `Proxy (port ${proxyPort}) — use account API key as Bearer token:`, + ` OpenAI: http://localhost:${proxyPort}/v1/chat/completions`, + ` Anthropic: http://localhost:${proxyPort}/v1/messages`, + ` Models: http://localhost:${proxyPort}/v1/models`, + ].join("\n"), ) + }, +}) - let hasWebDist = false - try { - await fs.access(webDistPath) - hasWebDist = true - } catch { - /* no built frontend */ - } +function mountProxyRoutes(app: Hono): void { + app.post("/chat/completions", proxyAuth, (c) => + completionsHandler(c, getState(c)), + ) + app.post("/v1/chat/completions", proxyAuth, (c) => + completionsHandler(c, getState(c)), + ) + app.get("/models", proxyAuth, (c) => modelsHandler(c, getState(c))) + app.get("/v1/models", proxyAuth, (c) => modelsHandler(c, getState(c))) + app.post("/embeddings", proxyAuth, (c) => embeddingsHandler(c, getState(c))) + app.post("/v1/embeddings", proxyAuth, (c) => + embeddingsHandler(c, getState(c)), + ) + app.post("/v1/messages", proxyAuth, (c) => messagesHandler(c, getState(c))) + app.post("/v1/messages/count_tokens", proxyAuth, (c) => + countTokensHandler(c, getState(c)), + ) +} - if (hasWebDist) { - app.get("/*", async (c) => { - const reqPath = c.req.path === "/" ? "/index.html" : c.req.path - const filePath = path.join(webDistPath, reqPath) - - try { - const content = await fs.readFile(filePath) - const ext = path.extname(filePath) - return c.body(content.buffer as ArrayBuffer, { - headers: { - "content-type": MIME_TYPES[ext] ?? "application/octet-stream", - }, - }) - } catch { - // SPA fallback - const indexContent = await fs.readFile( - path.join(webDistPath, "index.html"), - "utf8", - ) - return c.html(indexContent) - } - }) - } else { - app.get("/", (c) => - c.text( - "Console API running. Build the web UI with: cd web && bun install && bun run build", - ), - ) - } +async function mountStaticFiles(app: Hono): Promise { + const webDistPath = path.resolve( + new URL(".", import.meta.url).pathname, + "../../web/dist", + ) - // Auto-start enabled accounts - if (args["auto-start"]) { - const accounts = await getAccounts() - for (const account of accounts) { - if (account.enabled) { - try { - await startInstance(account) - } catch (error) { - consola.error( - `Failed to auto-start account "${account.name}":`, - error, - ) - } - } + let hasWebDist = false + try { + await fs.access(webDistPath) + hasWebDist = true + } catch { + /* no built frontend */ + } + + if (hasWebDist) { + app.get("/*", async (c) => { + const reqPath = c.req.path === "/" ? "/index.html" : c.req.path + const filePath = path.join(webDistPath, reqPath) + try { + const content = await fs.readFile(filePath) + const ext = path.extname(filePath) + const contentType = MIME_TYPES[ext] ?? "application/octet-stream" + const cacheControl = + reqPath.startsWith("/assets/") ? + "public, max-age=31536000, immutable" + : "no-cache" + return c.body(content.buffer as ArrayBuffer, { + headers: { + "content-type": contentType, + "cache-control": cacheControl, + }, + }) + } catch { + const indexContent = await fs.readFile( + path.join(webDistPath, "index.html"), + "utf8", + ) + return c.html(indexContent) + } + }) + } else { + app.get("/", (c) => + c.text( + "Console API running. Build the web UI with: cd web && bun install && bun run build", + ), + ) + } +} + +async function autoStartAccounts(): Promise { + const accounts = await getAccounts() + for (const account of accounts) { + if (account.enabled) { + try { + await startInstance(account) + } catch (error) { + consola.error(`Failed to auto-start account "${account.name}":`, error) } } + } +} - serve({ - fetch: app.fetch as ServerHandler, - port, - }) +async function proxyAuth( + c: import("hono").Context, + next: () => Promise, +): Promise { + const auth = c.req.header("authorization") + const token = auth?.replace(/^Bearer\s+/i, "") + if (!token) { + return c.json({ error: "Missing API key" }, 401) + } + const account = await getAccountByApiKey(token) + if (!account) { + return c.json({ error: "Invalid API key" }, 401) + } + const st = getInstanceState(account.id) + if (!st) { + return c.json({ error: "Account instance not running" }, 503) + } + c.set("proxyState", st) + return next() +} - consola.box(`🎛️ Console running at http://localhost:${port}`) - }, -}) +function getState(c: import("hono").Context): import("~/lib/state").State { + return c.get("proxyState") as import("~/lib/state").State +} diff --git a/src/console/instance-manager.ts b/src/console/instance-manager.ts index a93aab721..86014edcc 100644 --- a/src/console/instance-manager.ts +++ b/src/console/instance-manager.ts @@ -2,19 +2,9 @@ import type { Context } from "hono" import consola from "consola" import { events } from "fetch-event-stream" -import { Hono } from "hono" -import { cors } from "hono/cors" -import { logger } from "hono/logger" import { streamSSE } from "hono/streaming" -import { serve, type ServerHandler } from "srvx" import type { State } from "~/lib/state" -import type { AnthropicMessagesPayload } from "~/routes/messages/anthropic-types" -import type { - ChatCompletionResponse, - ChatCompletionsPayload, - ChatCompletionChunk, -} from "~/services/copilot/create-chat-completions" import type { ModelsResponse } from "~/services/copilot/get-models" import { @@ -26,11 +16,17 @@ import { } from "~/lib/api-config" import { HTTPError } from "~/lib/error" import { getTokenCount } from "~/lib/tokenizer" +import { type AnthropicMessagesPayload } from "~/routes/messages/anthropic-types" import { - translateToOpenAI, translateToAnthropic, + translateToOpenAI, } from "~/routes/messages/non-stream-translation" import { translateChunkToAnthropicEvents } from "~/routes/messages/stream-translation" +import { + type ChatCompletionChunk, + type ChatCompletionResponse, + type ChatCompletionsPayload, +} from "~/services/copilot/create-chat-completions" import { getVSCodeVersion } from "~/services/get-vscode-version" import type { Account } from "./account-store" @@ -38,7 +34,6 @@ import type { Account } from "./account-store" interface ProxyInstance { account: Account state: State - server: ReturnType | null status: "running" | "stopped" | "error" error?: string tokenInterval?: ReturnType @@ -107,26 +102,15 @@ export async function startInstance(account: Account): Promise { } const st = createState(account) - const instance: ProxyInstance = { - account, - state: st, - server: null, - status: "stopped", - } + const instance: ProxyInstance = { account, state: st, status: "stopped" } try { st.vsCodeVersion = await getVSCodeVersion() await setupInstanceToken(instance) st.models = await fetchModels(st) - - const app = createInstanceServer(instance) - instance.server = serve({ - fetch: app.fetch as ServerHandler, - port: account.port, - }) instance.status = "running" instances.set(account.id, instance) - consola.success(`[${account.name}] Proxy started on port ${account.port}`) + consola.success(`[${account.name}] Instance ready`) } catch (error) { instance.status = "error" instance.error = (error as Error).message @@ -136,16 +120,13 @@ export async function startInstance(account: Account): Promise { } } -export async function stopInstance(accountId: string): Promise { +export function stopInstance(accountId: string): void { const instance = instances.get(accountId) if (!instance) return - try { if (instance.tokenInterval) clearInterval(instance.tokenInterval) - if (instance.server) await instance.server.close() instance.status = "stopped" - instance.server = null - consola.info(`[${instance.account.name}] Proxy stopped`) + consola.info(`[${instance.account.name}] Instance stopped`) } catch (error) { consola.error(`[${instance.account.name}] Error stopping:`, error) } @@ -159,6 +140,12 @@ export function getInstanceError(accountId: string): string | undefined { return instances.get(accountId)?.error } +export function getInstanceState(accountId: string): State | undefined { + const instance = instances.get(accountId) + if (!instance || instance.status !== "running") return undefined + return instance.state +} + export async function getInstanceUsage(accountId: string): Promise { const instance = instances.get(accountId) if (!instance || instance.status !== "running") return null @@ -193,49 +180,10 @@ export async function getInstanceUser( } } -// === Per-instance server === - -function createInstanceServer(instance: ProxyInstance): Hono { - const app = new Hono() - app.use(logger()) - app.use(cors()) - - const st = instance.state - - app.get("/", (c) => c.text(`Proxy running for ${instance.account.name}`)) - - app.post("/chat/completions", (c) => completionsHandler(c, st)) - app.post("/v1/chat/completions", (c) => completionsHandler(c, st)) - app.get("/models", (c) => modelsHandler(c, st)) - app.get("/v1/models", (c) => modelsHandler(c, st)) - app.post("/embeddings", (c) => embeddingsHandler(c, st)) - app.post("/v1/embeddings", (c) => embeddingsHandler(c, st)) - app.post("/v1/messages", (c) => messagesHandler(c, st)) - app.post("/v1/messages/count_tokens", (c) => countTokensHandler(c, st)) - - app.get("/usage", async (c) => { - try { - const response = await fetch( - `${GITHUB_API_BASE_URL}/copilot_internal/user`, - { headers: githubHeaders(st) }, - ) - if (!response.ok) throw new HTTPError("Failed to get usage", response) - return c.json(await response.json()) - } catch (error) { - return c.json({ error: (error as Error).message }, 500) - } - }) - - app.get("/token", (c) => c.json({ token: st.copilotToken })) - - return app -} +// === Proxy handlers (used by unified router) === interface CompletionsPayload { - messages: Array<{ - role: string - content: unknown - }> + messages: Array<{ role: string; content: unknown }> model: string max_tokens?: number stream?: boolean @@ -256,7 +204,10 @@ function isAgentRequest(messages: CompletionsPayload["messages"]): boolean { return messages.some((msg) => ["assistant", "tool"].includes(msg.role)) } -async function completionsHandler(c: Context, st: State) { +export async function completionsHandler( + c: Context, + st: State, +): Promise { try { const payload = await c.req.json() @@ -305,7 +256,7 @@ async function completionsHandler(c: Context, st: State) { } } -function modelsHandler(c: Context, st: State) { +export function modelsHandler(c: Context, st: State): Response { const models = st.models?.data.map((model) => ({ id: model.id, @@ -319,7 +270,10 @@ function modelsHandler(c: Context, st: State) { return c.json({ object: "list", data: models, has_more: false }) } -async function embeddingsHandler(c: Context, st: State) { +export async function embeddingsHandler( + c: Context, + st: State, +): Promise { try { const payload = await c.req.json>() const response = await fetch(`${copilotBaseUrl(st)}/embeddings`, { @@ -338,7 +292,10 @@ async function embeddingsHandler(c: Context, st: State) { } } -async function messagesHandler(c: Context, st: State) { +export async function messagesHandler( + c: Context, + st: State, +): Promise { try { const anthropicPayload = await c.req.json() const openAIPayload = translateToOpenAI(anthropicPayload) @@ -419,7 +376,10 @@ async function messagesHandler(c: Context, st: State) { } } -async function countTokensHandler(c: Context, st: State) { +export async function countTokensHandler( + c: Context, + st: State, +): Promise { try { const anthropicPayload = await c.req.json() const openAIPayload: ChatCompletionsPayload = diff --git a/src/console/port-check.ts b/src/console/port-check.ts deleted file mode 100644 index e7281b5af..000000000 --- a/src/console/port-check.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { createServer } from "node:net" - -import { getAccounts, type Account } from "./account-store" - -/** - * Check if a port is available by attempting to bind to it. - */ -export function isPortAvailable(port: number): Promise { - return new Promise((resolve) => { - const server = createServer() - server.once("error", () => resolve(false)) - server.once("listening", () => { - server.close(() => resolve(true)) - }) - server.listen(port, "0.0.0.0") - }) -} - -export interface PortConflict { - port: number - conflict: "account" | "system" - accountName?: string -} - -/** - * Check a port against existing accounts and system availability. - * `excludeAccountId` allows skipping the current account when updating. - */ -export async function checkPortConflict( - port: number, - excludeAccountId?: string, -): Promise { - const accounts = await getAccounts() - const conflicting = accounts.find( - (a) => a.port === port && a.id !== excludeAccountId, - ) - - if (conflicting) { - return { port, conflict: "account", accountName: conflicting.name } - } - - const available = await isPortAvailable(port) - if (!available) { - return { port, conflict: "system" } - } - - return null -} - -/** - * Find the next available port starting from a given port. - */ -export async function findAvailablePort( - startPort: number, - excludeAccountId?: string, -): Promise { - const accounts = await getAccounts() - const usedPorts = new Set( - accounts - .filter((a: Account) => a.id !== excludeAccountId) - .map((a: Account) => a.port), - ) - - let port = startPort - while (port <= 65535) { - if (!usedPorts.has(port)) { - const available = await isPortAvailable(port) - if (available) return port - } - port++ - } - - throw new Error("No available port found") -} diff --git a/web/src/App.tsx b/web/src/App.tsx index aaf42ddec..c33dba51a 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,14 +1,67 @@ -import { useState, useEffect, useCallback } from "react" +import { useCallback, useEffect, useState } from "react" -import { api, type Account } from "./api" +import { api, getAdminKey, setAdminKey, type Account } from "./api" import { AccountCard } from "./components/AccountCard" import { AddAccountForm } from "./components/AddAccountForm" +function LoginForm({ onLogin }: { onLogin: () => void }) { + const [key, setKey] = useState("") + const [error, setError] = useState("") + const [loading, setLoading] = useState(false) + + const handleSubmit = async (e: React.SyntheticEvent) => { + e.preventDefault() + setError("") + setLoading(true) + setAdminKey(key.trim()) + try { + await api.checkAuth() + onLogin() + } catch { + setAdminKey("") + setError("Invalid admin key") + } finally { + setLoading(false) + } + } + + return ( +
+

+ Copilot API Console +

+

+ Enter admin key to continue +

+
void handleSubmit(e)}> + setKey(e.target.value)} + placeholder="Admin key" + autoFocus + style={{ marginBottom: 12 }} + /> + {error && ( +
+ {error} +
+ )} + +
+
+ ) +} + function AccountList({ accounts, + proxyPort, onRefresh, }: { accounts: Array + proxyPort: number onRefresh: () => Promise }) { if (accounts.length === 0) { @@ -31,16 +84,17 @@ function AccountList({ return (
{accounts.map((account) => ( - + ))}
) } -export function App() { +function Dashboard() { const [accounts, setAccounts] = useState>([]) const [showForm, setShowForm] = useState(false) const [loading, setLoading] = useState(true) + const [proxyPort, setProxyPort] = useState(4141) const refresh = useCallback(async () => { try { @@ -54,6 +108,7 @@ export function App() { }, []) useEffect(() => { + void api.getConfig().then((cfg) => setProxyPort(cfg.proxyPort)) void refresh() const interval = setInterval(() => void refresh(), 5000) return () => clearInterval(interval) @@ -102,7 +157,14 @@ export function App() { > Loading...

- : } + : }
) } + +export function App() { + const [authed, setAuthed] = useState(Boolean(getAdminKey())) + + if (!authed) return setAuthed(true)} /> + return +} diff --git a/web/src/api.ts b/web/src/api.ts index 4a5ba5c85..755987d3a 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -1,11 +1,21 @@ const BASE = "/api" +let adminKey = "" + +export function setAdminKey(key: string): void { + adminKey = key +} + +export function getAdminKey(): string { + return adminKey +} + export interface Account { id: string name: string githubToken: string accountType: string - port: number + apiKey: string enabled: boolean createdAt: string status?: "running" | "stopped" | "error" @@ -47,13 +57,14 @@ interface ErrorBody { } async function request(path: string, options?: RequestInit): Promise { - const res = await fetch(`${BASE}${path}`, { - ...options, - headers: { - "Content-Type": "application/json", - ...(options?.headers as Record), - }, - }) + const headers: Record = { + "Content-Type": "application/json", + ...(options?.headers as Record), + } + if (adminKey) { + headers["Authorization"] = `Bearer ${adminKey}` + } + const res = await fetch(`${BASE}${path}`, { ...options, headers }) if (!res.ok) { const body = (await res.json().catch(() => ({}))) as ErrorBody throw new Error(body.error ?? `HTTP ${res.status}`) @@ -62,24 +73,11 @@ async function request(path: string, options?: RequestInit): Promise { } export const api = { - getAccounts: () => request>("/accounts"), + checkAuth: () => request<{ ok: boolean }>("/auth/check"), - addAccount: (data: { - name: string - githubToken: string - accountType: string - port: number - }) => - request("/accounts", { - method: "POST", - body: JSON.stringify(data), - }), + getConfig: () => request<{ proxyPort: number }>("/config"), - updateAccount: (id: string, data: Partial) => - request(`/accounts/${id}`, { - method: "PUT", - body: JSON.stringify(data), - }), + getAccounts: () => request>("/accounts"), deleteAccount: (id: string) => request<{ ok: boolean }>(`/accounts/${id}`, { method: "DELETE" }), @@ -92,6 +90,9 @@ export const api = { getUsage: (id: string) => request(`/accounts/${id}/usage`), + regenerateKey: (id: string) => + request(`/accounts/${id}/regenerate-key`, { method: "POST" }), + startDeviceCode: () => request("/auth/device-code", { method: "POST" }), @@ -102,25 +103,9 @@ export const api = { sessionId: string name: string accountType: string - port: number }) => request("/auth/complete", { method: "POST", body: JSON.stringify(data), }), - - checkPort: (port: number, excludeId?: string) => - request<{ - available: boolean - conflict?: string - accountName?: string - }>(`/port/check/${port}${excludeId ? `?exclude=${excludeId}` : ""}`), - - suggestPort: (start?: number, excludeId?: string) => { - const params = new URLSearchParams() - if (start !== undefined) params.set("start", String(start)) - if (excludeId) params.set("exclude", excludeId) - const qs = params.toString() - return request<{ port: number }>(`/port/suggest${qs ? `?${qs}` : ""}`) - }, } diff --git a/web/src/components/AccountCard.tsx b/web/src/components/AccountCard.tsx index 7f7c1ddd9..da7ba8038 100644 --- a/web/src/components/AccountCard.tsx +++ b/web/src/components/AccountCard.tsx @@ -121,7 +121,29 @@ function UsagePanel({ usage }: { usage: UsageData }) { ) } -function EndpointsPanel({ port }: { port: number }) { +function useCopyFeedback(): [string | null, (text: string) => void] { + const [copied, setCopied] = useState(null) + const copy = (text: string) => { + void navigator.clipboard.writeText(text) + setCopied(text) + setTimeout(() => setCopied(null), 1500) + } + return [copied, copy] +} + +function ApiKeyPanel({ + apiKey, + onRegenerate, +}: { + apiKey: string + onRegenerate: () => void +}) { + const [visible, setVisible] = useState(false) + const [copied, copy] = useCopyFeedback() + const safeKey = apiKey ?? "" + const masked = safeKey.length > 8 ? `${safeKey.slice(0, 8)}${"•".repeat(24)}` : safeKey + const isCopied = copied === safeKey + return (
-
+ {isCopied ? "Copied!" : "API Key:"} + + copy(safeKey)} style={{ - marginBottom: 4, - color: "var(--text)", - fontWeight: 500, - fontSize: 13, + cursor: "pointer", + flex: 1, + color: isCopied ? "var(--green)" : undefined, }} + title="Click to copy" > - Endpoints -
-
OpenAI: http://localhost:{port}/v1/chat/completions
-
Anthropic: http://localhost:{port}/v1/messages
-
Models: http://localhost:{port}/v1/models
+ {visible ? safeKey : masked} + + +
) } @@ -159,6 +198,7 @@ function getUsageLabel(loading: boolean, visible: boolean): string { interface Props { account: Account + proxyPort: number onRefresh: () => Promise } @@ -166,15 +206,21 @@ function AccountActions({ account, status, onRefresh, + onToggleUsage, + usageLoading, + showUsage, }: { account: Account status: string onRefresh: () => Promise + onToggleUsage: () => void + usageLoading: boolean + showUsage: boolean }) { const [actionLoading, setActionLoading] = useState(false) const [confirmDelete, setConfirmDelete] = useState(false) - const handleAction = async (action: () => Promise) => { + const handleAction = async (action: () => Promise) => { setActionLoading(true) try { await action() @@ -192,20 +238,17 @@ function AccountActions({ setTimeout(() => setConfirmDelete(false), 3000) return } - setActionLoading(true) - try { - await api.deleteAccount(account.id) - await onRefresh() - } catch (err) { - console.error("Delete failed:", err) - } finally { - setActionLoading(false) - setConfirmDelete(false) - } + await handleAction(() => api.deleteAccount(account.id)) + setConfirmDelete(false) } return (
+ {status === "running" && ( + + )} {status === "running" ? - {showUsage - && (usage ? - - :
- Usage data unavailable. Make sure the instance is running. -
)} - - ) -} - -export function AccountCard({ account, onRefresh }: Props) { - const status = account.status ?? "stopped" + const handleRegenerate = () => { + void (async () => { + try { + await api.regenerateKey(account.id) + await onRefresh() + } catch (err) { + console.error("Regenerate failed:", err) + } + })() + } return (
{account.user?.login ? `@${account.user.login} · ` : ""} - Port {account.port} · {account.accountType} + {account.accountType}
{account.error && (
@@ -324,11 +418,28 @@ export function AccountCard({ account, onRefresh }: Props) { account={account} status={status} onRefresh={onRefresh} + onToggleUsage={() => void handleToggleUsage()} + usageLoading={usageLoading} + showUsage={showUsage} />
- {status === "running" && } - {status === "running" && } + + {status === "running" && ( + + )} + {showUsage + && (usage ? + + :
+ Usage data unavailable. Make sure the instance is running. +
)}
) } diff --git a/web/src/components/AddAccountForm.tsx b/web/src/components/AddAccountForm.tsx index 324c0b04d..73723673c 100644 --- a/web/src/components/AddAccountForm.tsx +++ b/web/src/components/AddAccountForm.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useCallback } from "react" +import { useCallback, useEffect, useRef, useState } from "react" import { api } from "../api" @@ -8,30 +8,6 @@ interface Props { } type Step = "config" | "authorize" | "done" -type PortStatus = "idle" | "checking" | "ok" | "conflict" - -function PortIndicator({ status }: { status: PortStatus }) { - if (status === "idle") return null - const colorMap: Record = { - ok: "var(--green)", - conflict: "var(--red)", - checking: "var(--yellow)", - } - return ( - - ) -} function DeviceCodeDisplay({ userCode, @@ -160,173 +136,64 @@ function AuthorizeStep({ ) } -function usePortCheck() { - const [portStatus, setPortStatus] = useState("idle") - const [portMessage, setPortMessage] = useState("") - const timerRef = useRef | null>(null) - - const check = useCallback((value: string) => { - if (timerRef.current) clearTimeout(timerRef.current) - const portNum = Number.parseInt(value, 10) - if (Number.isNaN(portNum) || portNum < 1024 || portNum > 65535) { - setPortStatus("conflict") - setPortMessage("Port must be 1024–65535") - return - } - setPortStatus("checking") - setPortMessage("") - timerRef.current = setTimeout(() => { - void (async () => { - try { - const result = await api.checkPort(portNum) - if (result.available) { - setPortStatus("ok") - setPortMessage("") - } else { - setPortStatus("conflict") - setPortMessage( - result.conflict === "account" ? - `Used by "${result.accountName}"` - : "Occupied by another process", - ) - } - } catch { - setPortStatus("idle") - } - })() - }, 400) - }, []) - - return { portStatus, portMessage, check, setPortStatus, setPortMessage } -} - -function getPortBorderColor(status: PortStatus): string | undefined { - if (status === "conflict") return "var(--red)" - if (status === "ok") return "var(--green)" - return undefined -} - -function PortField({ - port, - portStatus, - portMessage, - onPortChange, - onAutoPort, +function ConfigForm({ + onSubmit, + onCancel, + loading, + error, + name, + setName, + accountType, + setAccountType, }: { - port: string - portStatus: PortStatus - portMessage: string - onPortChange: (v: string) => void - onAutoPort: () => void -}) { - return ( -
- -
- onPortChange(e.target.value)} - placeholder="4141" - style={{ borderColor: getPortBorderColor(portStatus) }} - /> - -
- {portMessage && ( -
- {portMessage} -
- )} -
- ) -} - -interface ConfigFormProps { onSubmit: (e: React.SyntheticEvent) => void onCancel: () => void loading: boolean error: string - portStatus: PortStatus - portMessage: string name: string setName: (v: string) => void accountType: string setAccountType: (v: string) => void - port: string - onPortChange: (v: string) => void - onAutoPort: () => void -} - -function ConfigForm(props: ConfigFormProps) { - const isDisabled = - props.loading - || props.portStatus === "conflict" - || props.portStatus === "checking" - +}) { return ( -
+

Add Account

-
+
props.setName(e.target.value)} + value={name} + onChange={(e) => setName(e.target.value)} placeholder="e.g. Personal" />
- -
-
- - +
+ + +
- {props.error && ( + {error && (
- {props.error} + {error}
)}
- -
@@ -351,11 +218,7 @@ function useAuthFlow(onComplete: () => Promise) { useEffect(() => cleanup, [cleanup]) - const startAuth = async ( - name: string, - accountType: string, - portNum: number, - ) => { + const startAuth = async (name: string, accountType: string) => { setError("") setLoading(true) try { @@ -376,7 +239,6 @@ function useAuthFlow(onComplete: () => Promise) { sessionId: result.sessionId, name, accountType, - port: portNum, }) setStep("done") await onComplete() @@ -413,82 +275,39 @@ function useAuthFlow(onComplete: () => Promise) { export function AddAccountForm({ onComplete, onCancel }: Props) { const [name, setName] = useState("") const [accountType, setAccountType] = useState("individual") - const [port, setPort] = useState("") - const { portStatus, portMessage, check, setPortStatus, setPortMessage } = - usePortCheck() const auth = useAuthFlow(onComplete) - useEffect(() => { - void api.suggestPort(4141).then((res) => { - setPort(String(res.port)) - setPortStatus("ok") - setPortMessage("") - }) - }, [setPortStatus, setPortMessage]) - - const handlePortChange = (value: string) => { - setPort(value) - check(value) - } - - const handleAutoPort = () => { - void (async () => { - try { - const res = await api.suggestPort(Number.parseInt(port, 10) || 4141) - setPort(String(res.port)) - setPortStatus("ok") - setPortMessage("") - } catch { - auth.setError("Failed to find available port") - } - })() - } - const handleSubmit = (e: React.SyntheticEvent) => { e.preventDefault() if (!name.trim()) { auth.setError("Account name is required") return } - const portNum = Number.parseInt(port, 10) - if (Number.isNaN(portNum) || portNum < 1024 || portNum > 65535) { - auth.setError("Port must be between 1024 and 65535") - return - } - if (portStatus === "conflict") { - auth.setError("Please resolve the port conflict first") - return - } - void auth.startAuth(name.trim(), accountType, portNum) + void auth.startAuth(name.trim(), accountType) } if (auth.step === "done") return null - const wrapperStyle = { - background: "var(--bg-card)", - border: "1px solid var(--border)", - borderRadius: "var(--radius)", - padding: 20, - marginBottom: 16, - } - return ( -
+
{auth.step === "config" && ( )} {auth.step === "authorize" && ( diff --git a/web/vite.config.ts b/web/vite.config.ts index d9b9c19d8..5beb39cce 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -10,5 +10,10 @@ export default defineConfig({ }, build: { outDir: "dist", + rollupOptions: { + output: { + entryFileNames: "assets/[name]-[hash]-v2.js", + }, + }, }, }) From d584cd03ab7feb43e8c28a9ee3814a586e743603 Mon Sep 17 00:00:00 2001 From: haruka <1628615876@qq.com> Date: Sat, 14 Feb 2026 04:26:20 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=89=B4=E6=9D=83?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile.console | 2 +- src/console/admin-auth.ts | 92 ++++++++++++++++++ src/console/api.ts | 62 ++++++++++--- src/console/index.ts | 13 +-- web/src/App.tsx | 190 ++++++++++++++++++++++++++++++++++---- web/src/api.ts | 46 +++++++-- 6 files changed, 353 insertions(+), 52 deletions(-) create mode 100644 src/console/admin-auth.ts diff --git a/Dockerfile.console b/Dockerfile.console index 858a8f7b7..45182e161 100644 --- a/Dockerfile.console +++ b/Dockerfile.console @@ -33,7 +33,7 @@ EXPOSE 3000 4141 VOLUME /root/.local/share/copilot-api HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ - CMD wget --spider -q http://localhost:3000/api/auth/check || exit 1 + CMD wget --spider -q http://localhost:3000/api/config || exit 1 ENTRYPOINT ["bun", "run", "./src/main.ts", "console"] CMD ["--web-port", "3000", "--proxy-port", "4141"] diff --git a/src/console/admin-auth.ts b/src/console/admin-auth.ts new file mode 100644 index 000000000..03a09651e --- /dev/null +++ b/src/console/admin-auth.ts @@ -0,0 +1,92 @@ +import crypto from "node:crypto" +import fs from "node:fs/promises" +import path from "node:path" + +import { PATHS } from "~/lib/paths" + +interface AdminCredentials { + username: string + passwordHash: string +} + +interface AdminStore { + credentials: AdminCredentials | null + sessions: Array<{ token: string; createdAt: string }> +} + +const STORE_PATH = path.join(PATHS.APP_DIR, "admin.json") +const SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000 // 7 days + +async function readStore(): Promise { + try { + const data = await fs.readFile(STORE_PATH) + return JSON.parse(data) as AdminStore + } catch { + return { credentials: null, sessions: [] } + } +} + +async function writeStore(store: AdminStore): Promise { + await fs.writeFile(STORE_PATH, JSON.stringify(store, null, 2)) +} + +export async function isSetupRequired(): Promise { + const store = await readStore() + return store.credentials === null +} + +export async function setupAdmin( + username: string, + password: string, +): Promise { + const store = await readStore() + if (store.credentials !== null) { + throw new Error("Admin already configured") + } + const passwordHash = await Bun.password.hash(password) + const token = generateSessionToken() + store.credentials = { username, passwordHash } + store.sessions = [{ token, createdAt: new Date().toISOString() }] + await writeStore(store) + return token +} + +export async function loginAdmin( + username: string, + password: string, +): Promise { + const store = await readStore() + if (!store.credentials) return null + if (store.credentials.username !== username) return null + const valid = await Bun.password.verify( + password, + store.credentials.passwordHash, + ) + if (!valid) return null + const token = generateSessionToken() + cleanExpiredSessions(store) + store.sessions.push({ token, createdAt: new Date().toISOString() }) + await writeStore(store) + return token +} + +export async function validateSession(token: string): Promise { + const store = await readStore() + const now = Date.now() + return store.sessions.some((s) => { + const age = now - new Date(s.createdAt).getTime() + return s.token === token && age < SESSION_TTL_MS + }) +} + +function generateSessionToken(): string { + return `session-${crypto.randomBytes(24).toString("hex")}` +} + +function cleanExpiredSessions(store: AdminStore): void { + const now = Date.now() + store.sessions = store.sessions.filter((s) => { + const age = now - new Date(s.createdAt).getTime() + return age < SESSION_TTL_MS + }) +} diff --git a/src/console/api.ts b/src/console/api.ts index bea4775ac..08bfed125 100644 --- a/src/console/api.ts +++ b/src/console/api.ts @@ -10,6 +10,12 @@ import { regenerateApiKey, updateAccount, } from "./account-store" +import { + isSetupRequired, + loginAdmin, + setupAdmin, + validateSession, +} from "./admin-auth" import { cleanupSession, getSession, startDeviceFlow } from "./auth-flow" import { getInstanceError, @@ -20,11 +26,6 @@ import { stopInstance, } from "./instance-manager" -let adminKey = "" - -export function setAdminKey(key: string): void { - adminKey = key -} let proxyPort = 4141 export function setProxyPort(port: number): void { @@ -41,21 +42,60 @@ export const consoleApi = new Hono() consoleApi.use(cors()) -// Public config (no auth required) -consoleApi.get("/config", (c) => c.json({ proxyPort })) +// Public endpoints (no auth required) +consoleApi.get("/config", async (c) => { + const needsSetup = await isSetupRequired() + return c.json({ proxyPort, needsSetup }) +}) + +const SetupSchema = z.object({ + username: z.string().min(1), + password: z.string().min(6), +}) + +consoleApi.post("/auth/setup", async (c) => { + const body: unknown = await c.req.json() + const parsed = SetupSchema.safeParse(body) + if (!parsed.success) { + return c.json({ error: formatZodError(parsed.error) }, 400) + } + try { + const token = await setupAdmin(parsed.data.username, parsed.data.password) + return c.json({ token }) + } catch (error) { + return c.json({ error: (error as Error).message }, 400) + } +}) + +const LoginSchema = z.object({ + username: z.string().min(1), + password: z.string().min(1), +}) + +consoleApi.post("/auth/login", async (c) => { + const body: unknown = await c.req.json() + const parsed = LoginSchema.safeParse(body) + if (!parsed.success) { + return c.json({ error: formatZodError(parsed.error) }, 400) + } + const token = await loginAdmin(parsed.data.username, parsed.data.password) + if (!token) { + return c.json({ error: "Invalid username or password" }, 401) + } + return c.json({ token }) +}) -// Admin auth middleware +// Admin auth middleware (session token) consoleApi.use("/*", async (c, next) => { - if (!adminKey) return next() const auth = c.req.header("authorization") const token = auth?.replace("Bearer ", "") - if (token !== adminKey) { + if (!token || !(await validateSession(token))) { return c.json({ error: "Unauthorized" }, 401) } return next() }) -// Auth check endpoint (for frontend login) +// Auth check endpoint consoleApi.get("/auth/check", (c) => c.json({ ok: true })) // List all accounts with status diff --git a/src/console/index.ts b/src/console/index.ts index b55fb242e..eeefefa74 100644 --- a/src/console/index.ts +++ b/src/console/index.ts @@ -3,7 +3,6 @@ import consola from "consola" import { Hono } from "hono" import { cors } from "hono/cors" import { logger } from "hono/logger" -import crypto from "node:crypto" import fs from "node:fs/promises" import path from "node:path" import { serve, type ServerHandler } from "srvx" @@ -11,7 +10,7 @@ import { serve, type ServerHandler } from "srvx" import { ensurePaths } from "~/lib/paths" import { getAccountByApiKey, getAccounts } from "./account-store" -import { consoleApi, setAdminKey, setProxyPort } from "./api" +import { consoleApi, setProxyPort } from "./api" import { completionsHandler, countTokensHandler, @@ -50,10 +49,6 @@ export const console_ = defineCommand({ default: "4141", description: "Port for the proxy API endpoints", }, - "admin-key": { - type: "string", - description: "Admin key for console access (auto-generated if not set)", - }, verbose: { alias: "v", type: "boolean", @@ -76,10 +71,6 @@ export const console_ = defineCommand({ const webPort = Number.parseInt(args["web-port"], 10) const proxyPort = Number.parseInt(args["proxy-port"], 10) - const adminKeyArg = args["admin-key"] as string | undefined - const generatedAdminKey = - adminKeyArg ?? `admin-${crypto.randomBytes(8).toString("hex")}` - setAdminKey(generatedAdminKey) setProxyPort(proxyPort) // === Web console server === @@ -105,7 +96,7 @@ export const console_ = defineCommand({ consola.box( [ `🎛️ Console: http://localhost:${webPort}`, - `🔑 Admin Key: ${generatedAdminKey}`, + `🔐 First visit to set up admin account`, "", `Proxy (port ${proxyPort}) — use account API key as Bearer token:`, ` OpenAI: http://localhost:${proxyPort}/v1/chat/completions`, diff --git a/web/src/App.tsx b/web/src/App.tsx index c33dba51a..ee6c5eb1b 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,11 +1,91 @@ import { useCallback, useEffect, useState } from "react" -import { api, getAdminKey, setAdminKey, type Account } from "./api" +import { api, getSessionToken, setSessionToken, type Account } from "./api" import { AccountCard } from "./components/AccountCard" import { AddAccountForm } from "./components/AddAccountForm" +type AuthState = "loading" | "setup" | "login" | "authed" + +function SetupForm({ onComplete }: { onComplete: () => void }) { + const [username, setUsername] = useState("") + const [password, setPassword] = useState("") + const [confirm, setConfirm] = useState("") + const [error, setError] = useState("") + const [loading, setLoading] = useState(false) + + const handleSubmit = async (e: React.SyntheticEvent) => { + e.preventDefault() + setError("") + if (password !== confirm) { + setError("Passwords do not match") + return + } + if (password.length < 6) { + setError("Password must be at least 6 characters") + return + } + setLoading(true) + try { + const { token } = await api.setup(username, password) + setSessionToken(token) + onComplete() + } catch (err) { + setError((err as Error).message) + } finally { + setLoading(false) + } + } + + return ( +
+

+ Copilot API Console +

+

+ Create your admin account to get started +

+
void handleSubmit(e)}> + setUsername(e.target.value)} + placeholder="Username" + autoFocus + autoComplete="username" + style={{ marginBottom: 12 }} + /> + setPassword(e.target.value)} + placeholder="Password (min 6 chars)" + autoComplete="new-password" + style={{ marginBottom: 12 }} + /> + setConfirm(e.target.value)} + placeholder="Confirm password" + autoComplete="new-password" + style={{ marginBottom: 12 }} + /> + {error && ( +
+ {error} +
+ )} + +
+
+ ) +} + function LoginForm({ onLogin }: { onLogin: () => void }) { - const [key, setKey] = useState("") + const [username, setUsername] = useState("") + const [password, setPassword] = useState("") const [error, setError] = useState("") const [loading, setLoading] = useState(false) @@ -13,13 +93,12 @@ function LoginForm({ onLogin }: { onLogin: () => void }) { e.preventDefault() setError("") setLoading(true) - setAdminKey(key.trim()) try { - await api.checkAuth() + const { token } = await api.login(username, password) + setSessionToken(token) onLogin() } catch { - setAdminKey("") - setError("Invalid admin key") + setError("Invalid username or password") } finally { setLoading(false) } @@ -31,15 +110,24 @@ function LoginForm({ onLogin }: { onLogin: () => void }) { Copilot API Console

- Enter admin key to continue + Sign in to continue

void handleSubmit(e)}> setKey(e.target.value)} - placeholder="Admin key" + type="text" + value={username} + onChange={(e) => setUsername(e.target.value)} + placeholder="Username" autoFocus + autoComplete="username" + style={{ marginBottom: 12 }} + /> + setPassword(e.target.value)} + placeholder="Password" + autoComplete="current-password" style={{ marginBottom: 12 }} /> {error && ( @@ -48,7 +136,7 @@ function LoginForm({ onLogin }: { onLogin: () => void }) {
)}
@@ -84,7 +172,12 @@ function AccountList({ return (
{accounts.map((account) => ( - + ))}
) @@ -119,6 +212,11 @@ function Dashboard() { await refresh() } + const handleLogout = () => { + setSessionToken("") + window.location.reload() + } + return (
- +
+ + +
{showForm && ( @@ -157,14 +258,65 @@ function Dashboard() { > Loading...

- : } + : + }
) } export function App() { - const [authed, setAuthed] = useState(Boolean(getAdminKey())) + const [authState, setAuthState] = useState("loading") + + useEffect(() => { + void (async () => { + try { + const config = await api.getConfig() + if (config.needsSetup) { + setAuthState("setup") + return + } + const token = getSessionToken() + if (token) { + try { + await api.checkAuth() + setAuthState("authed") + return + } catch { + setSessionToken("") + } + } + setAuthState("login") + } catch { + setAuthState("login") + } + })() + }, []) + + if (authState === "loading") { + return ( +
+ Loading... +
+ ) + } + + if (authState === "setup") { + return setAuthState("authed")} /> + } + + if (authState === "login") { + return setAuthState("authed")} /> + } - if (!authed) return setAuthed(true)} /> return } diff --git a/web/src/api.ts b/web/src/api.ts index 755987d3a..baf013222 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -1,13 +1,21 @@ const BASE = "/api" -let adminKey = "" - -export function setAdminKey(key: string): void { - adminKey = key +let sessionToken = "" + +export function setSessionToken(token: string): void { + sessionToken = token + if (token) { + localStorage.setItem("sessionToken", token) + } else { + localStorage.removeItem("sessionToken") + } } -export function getAdminKey(): string { - return adminKey +export function getSessionToken(): string { + if (!sessionToken) { + sessionToken = localStorage.getItem("sessionToken") ?? "" + } + return sessionToken } export interface Account { @@ -52,6 +60,11 @@ export interface AuthPollResponse { error?: string } +export interface ConfigResponse { + proxyPort: number + needsSetup: boolean +} + interface ErrorBody { error?: string } @@ -61,8 +74,9 @@ async function request(path: string, options?: RequestInit): Promise { "Content-Type": "application/json", ...(options?.headers as Record), } - if (adminKey) { - headers["Authorization"] = `Bearer ${adminKey}` + const token = getSessionToken() + if (token) { + headers["Authorization"] = `Bearer ${token}` } const res = await fetch(`${BASE}${path}`, { ...options, headers }) if (!res.ok) { @@ -73,9 +87,21 @@ async function request(path: string, options?: RequestInit): Promise { } export const api = { - checkAuth: () => request<{ ok: boolean }>("/auth/check"), + getConfig: () => request("/config"), - getConfig: () => request<{ proxyPort: number }>("/config"), + setup: (username: string, password: string) => + request<{ token: string }>("/auth/setup", { + method: "POST", + body: JSON.stringify({ username, password }), + }), + + login: (username: string, password: string) => + request<{ token: string }>("/auth/login", { + method: "POST", + body: JSON.stringify({ username, password }), + }), + + checkAuth: () => request<{ ok: boolean }>("/auth/check"), getAccounts: () => request>("/accounts"), From 629bcdc9fe901052b51dd28780827470efa6f980 Mon Sep 17 00:00:00 2001 From: haruka <1628615876@qq.com> Date: Mon, 16 Feb 2026 07:16:03 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E8=BD=AE=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/console/account-store.ts | 59 ++++++++- src/console/api.ts | 32 +++++ src/console/index.ts | 102 +++++++++++++--- src/console/instance-manager.ts | 16 ++- src/console/load-balancer.ts | 40 +++++++ web/src/App.tsx | 185 ++++++++++++++++++++++++++++- web/src/api.ts | 24 ++++ web/src/components/AccountCard.tsx | 79 ++++++++++++ 8 files changed, 515 insertions(+), 22 deletions(-) create mode 100644 src/console/load-balancer.ts diff --git a/src/console/account-store.ts b/src/console/account-store.ts index 35b55e360..1297c6993 100644 --- a/src/console/account-store.ts +++ b/src/console/account-store.ts @@ -12,10 +12,18 @@ export interface Account { apiKey: string enabled: boolean createdAt: string + priority: number +} + +export interface PoolConfig { + enabled: boolean + strategy: "round-robin" | "priority" + apiKey: string } export interface AccountStore { accounts: Array + pool?: PoolConfig } const STORE_PATH = path.join(PATHS.APP_DIR, "accounts.json") @@ -55,7 +63,9 @@ export async function getAccountByApiKey( } export async function addAccount( - account: Omit, + account: Omit & { + priority?: number + }, ): Promise { const store = await readStore() const newAccount: Account = { @@ -63,6 +73,7 @@ export async function addAccount( id: crypto.randomUUID(), apiKey: generateApiKey(), createdAt: new Date().toISOString(), + priority: account.priority ?? 0, } store.accounts.push(newAccount) await writeStore(store) @@ -100,3 +111,49 @@ export async function regenerateApiKey( await writeStore(store) return store.accounts[index] } + +export async function getPoolConfig(): Promise { + const store = await readStore() + if (!store.pool) return undefined + // Backfill apiKey for legacy stores + if (!store.pool.apiKey) { + store.pool.apiKey = generatePoolApiKey() + await writeStore(store) + } + return store.pool +} + +export async function updatePoolConfig( + config: Partial, +): Promise { + const store = await readStore() + const current: PoolConfig = store.pool ?? { + enabled: false, + strategy: "round-robin", + apiKey: generatePoolApiKey(), + } + store.pool = { ...current, ...config } + // Ensure apiKey exists for legacy stores + if (!store.pool.apiKey) { + store.pool.apiKey = generatePoolApiKey() + } + await writeStore(store) + return store.pool +} + +export async function regeneratePoolApiKey(): Promise { + const store = await readStore() + const current: PoolConfig = store.pool ?? { + enabled: false, + strategy: "round-robin", + apiKey: generatePoolApiKey(), + } + current.apiKey = generatePoolApiKey() + store.pool = current + await writeStore(store) + return store.pool +} + +function generatePoolApiKey(): string { + return `cpp-${crypto.randomBytes(16).toString("hex")}` +} diff --git a/src/console/api.ts b/src/console/api.ts index 08bfed125..4a2ce3746 100644 --- a/src/console/api.ts +++ b/src/console/api.ts @@ -7,8 +7,11 @@ import { deleteAccount, getAccount, getAccounts, + getPoolConfig, regenerateApiKey, + regeneratePoolApiKey, updateAccount, + updatePoolConfig, } from "./account-store" import { isSetupRequired, @@ -133,6 +136,7 @@ const AddAccountSchema = z.object({ githubToken: z.string().min(1), accountType: z.string().default("individual"), enabled: z.boolean().default(true), + priority: z.number().int().min(0).default(0), }) // Add account @@ -151,6 +155,7 @@ const UpdateAccountSchema = z.object({ githubToken: z.string().min(1).optional(), accountType: z.string().optional(), enabled: z.boolean().optional(), + priority: z.number().int().min(0).optional(), }) // Update account @@ -258,3 +263,30 @@ consoleApi.post("/auth/complete", async (c) => { cleanupSession(parsed.data.sessionId) return c.json(account, 201) }) + +// === Pool Configuration === + +consoleApi.get("/pool", async (c) => { + const config = await getPoolConfig() + return c.json(config ?? { enabled: false, strategy: "round-robin" }) +}) + +const UpdatePoolSchema = z.object({ + enabled: z.boolean().optional(), + strategy: z.enum(["round-robin", "priority"]).optional(), +}) + +consoleApi.put("/pool", async (c) => { + const body: unknown = await c.req.json() + const parsed = UpdatePoolSchema.safeParse(body) + if (!parsed.success) { + return c.json({ error: formatZodError(parsed.error) }, 400) + } + const config = await updatePoolConfig(parsed.data) + return c.json(config) +}) + +consoleApi.post("/pool/regenerate-key", async (c) => { + const config = await regeneratePoolApiKey() + return c.json(config) +}) diff --git a/src/console/index.ts b/src/console/index.ts index eeefefa74..76178d98d 100644 --- a/src/console/index.ts +++ b/src/console/index.ts @@ -9,7 +9,7 @@ import { serve, type ServerHandler } from "srvx" import { ensurePaths } from "~/lib/paths" -import { getAccountByApiKey, getAccounts } from "./account-store" +import { getAccountByApiKey, getAccounts, getPoolConfig } from "./account-store" import { consoleApi, setProxyPort } from "./api" import { completionsHandler, @@ -20,6 +20,7 @@ import { modelsHandler, startInstance, } from "./instance-manager" +import { selectAccount } from "./load-balancer" const MIME_TYPES: Record = { ".html": "text/html", @@ -109,20 +110,20 @@ export const console_ = defineCommand({ function mountProxyRoutes(app: Hono): void { app.post("/chat/completions", proxyAuth, (c) => - completionsHandler(c, getState(c)), + withPoolRetry(c, completionsHandler), ) app.post("/v1/chat/completions", proxyAuth, (c) => - completionsHandler(c, getState(c)), + withPoolRetry(c, completionsHandler), ) app.get("/models", proxyAuth, (c) => modelsHandler(c, getState(c))) app.get("/v1/models", proxyAuth, (c) => modelsHandler(c, getState(c))) - app.post("/embeddings", proxyAuth, (c) => embeddingsHandler(c, getState(c))) + app.post("/embeddings", proxyAuth, (c) => withPoolRetry(c, embeddingsHandler)) app.post("/v1/embeddings", proxyAuth, (c) => - embeddingsHandler(c, getState(c)), + withPoolRetry(c, embeddingsHandler), ) - app.post("/v1/messages", proxyAuth, (c) => messagesHandler(c, getState(c))) + app.post("/v1/messages", proxyAuth, (c) => withPoolRetry(c, messagesHandler)) app.post("/v1/messages/count_tokens", proxyAuth, (c) => - countTokensHandler(c, getState(c)), + withPoolRetry(c, countTokensHandler), ) } @@ -194,21 +195,90 @@ async function proxyAuth( ): Promise { const auth = c.req.header("authorization") const token = auth?.replace(/^Bearer\s+/i, "") - if (!token) { - return c.json({ error: "Missing API key" }, 401) + + // Try per-account key first + if (token) { + const account = await getAccountByApiKey(token) + if (account) { + const st = getInstanceState(account.id) + if (!st) { + return c.json({ error: "Account instance not running" }, 503) + } + c.set("proxyState", st) + await next() + return + } } - const account = await getAccountByApiKey(token) - if (!account) { + + // Fall back to pool mode + const poolConfig = await getPoolConfig() + if (!poolConfig?.enabled || !token || token !== poolConfig.apiKey) { return c.json({ error: "Invalid API key" }, 401) } - const st = getInstanceState(account.id) - if (!st) { - return c.json({ error: "Account instance not running" }, 503) + + const result = await selectAccount(poolConfig.strategy) + if (!result) { + return c.json({ error: "No available accounts in pool" }, 503) } - c.set("proxyState", st) - return next() + + c.set("proxyState", result.state) + c.set("poolMode", true) + c.set("poolStrategy", poolConfig.strategy) + c.set("poolAccountId", result.account.id) + await next() } function getState(c: import("hono").Context): import("~/lib/state").State { return c.get("proxyState") as import("~/lib/state").State } + +type Handler = ( + c: import("hono").Context, + st: import("~/lib/state").State, +) => Promise | Response + +async function withPoolRetry( + c: import("hono").Context, + handler: Handler, +): Promise { + const isPool = c.get("poolMode") as boolean | undefined + if (!isPool) { + return handler(c, getState(c)) + } + + // Buffer the body for potential retries + const body: unknown = await c.req.json() + c.set("bufferedBody", body) + + const strategy = c.get("poolStrategy") as "round-robin" | "priority" + const exclude = new Set() + + // First attempt with the account already selected by proxyAuth + const firstResponse = await handler(c, getState(c)) + if (!isRetryableStatus(firstResponse.status)) { + return firstResponse + } + + // Add the first account to exclude list and retry with others + exclude.add(c.get("poolAccountId") as string) + + while (true) { + const next = await selectAccount(strategy, exclude) + if (!next) { + // No more accounts to try, return the last error response + return firstResponse + } + + exclude.add(next.account.id) + c.set("proxyState", next.state) + + const retryResponse = await handler(c, next.state) + if (!isRetryableStatus(retryResponse.status)) { + return retryResponse + } + } +} + +function isRetryableStatus(status: number): boolean { + return status === 429 || status >= 500 +} diff --git a/src/console/instance-manager.ts b/src/console/instance-manager.ts index 86014edcc..d10e89d2d 100644 --- a/src/console/instance-manager.ts +++ b/src/console/instance-manager.ts @@ -209,7 +209,9 @@ export async function completionsHandler( st: State, ): Promise { try { - const payload = await c.req.json() + const payload = + (c.get("bufferedBody") as CompletionsPayload | undefined) + ?? (await c.req.json()) const headers: Record = { ...copilotHeaders(st, hasVisionContent(payload.messages)), @@ -275,7 +277,9 @@ export async function embeddingsHandler( st: State, ): Promise { try { - const payload = await c.req.json>() + const payload = + (c.get("bufferedBody") as Record | undefined) + ?? (await c.req.json>()) const response = await fetch(`${copilotBaseUrl(st)}/embeddings`, { method: "POST", headers: copilotHeaders(st), @@ -297,7 +301,9 @@ export async function messagesHandler( st: State, ): Promise { try { - const anthropicPayload = await c.req.json() + const anthropicPayload = + (c.get("bufferedBody") as AnthropicMessagesPayload | undefined) + ?? (await c.req.json()) const openAIPayload = translateToOpenAI(anthropicPayload) if (!openAIPayload.max_tokens) { @@ -381,7 +387,9 @@ export async function countTokensHandler( st: State, ): Promise { try { - const anthropicPayload = await c.req.json() + const anthropicPayload = + (c.get("bufferedBody") as AnthropicMessagesPayload | undefined) + ?? (await c.req.json()) const openAIPayload: ChatCompletionsPayload = translateToOpenAI(anthropicPayload) diff --git a/src/console/load-balancer.ts b/src/console/load-balancer.ts new file mode 100644 index 000000000..27b32a5be --- /dev/null +++ b/src/console/load-balancer.ts @@ -0,0 +1,40 @@ +import { type Account, getAccounts } from "./account-store" +import { getInstanceState } from "./instance-manager" + +let rrIndex = 0 + +export function getRunningAccounts(accounts: Array): Array { + return accounts.filter( + (a) => a.enabled && getInstanceState(a.id) !== undefined, + ) +} + +export async function selectAccount( + strategy: "round-robin" | "priority", + exclude?: Set, +): Promise<{ account: Account; state: import("~/lib/state").State } | null> { + const all = await getAccounts() + let running = getRunningAccounts(all) + + if (exclude?.size) { + running = running.filter((a) => !exclude.has(a.id)) + } + + if (running.length === 0) return null + + let selected: Account + + if (strategy === "priority") { + running.sort((a, b) => b.priority - a.priority) + selected = running[0] + } else { + rrIndex = rrIndex % running.length + selected = running[rrIndex] + rrIndex = (rrIndex + 1) % running.length + } + + const state = getInstanceState(selected.id) + if (!state) return null + + return { account: selected, state } +} diff --git a/web/src/App.tsx b/web/src/App.tsx index ee6c5eb1b..b6df82c7c 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from "react" -import { api, getSessionToken, setSessionToken, type Account } from "./api" +import { api, getSessionToken, setSessionToken, type Account, type PoolConfig } from "./api" import { AccountCard } from "./components/AccountCard" import { AddAccountForm } from "./components/AddAccountForm" @@ -183,11 +183,191 @@ function AccountList({ ) } +function PoolSettings({ + pool, + proxyPort, + onChange, +}: { + pool: PoolConfig + proxyPort: number + onChange: (p: PoolConfig) => void +}) { + const [saving, setSaving] = useState(false) + const [keyVisible, setKeyVisible] = useState(false) + const [copied, setCopied] = useState(false) + + const toggle = async () => { + setSaving(true) + try { + const updated = await api.updatePool({ enabled: !pool.enabled }) + onChange(updated) + } finally { + setSaving(false) + } + } + + const changeStrategy = async (strategy: PoolConfig["strategy"]) => { + setSaving(true) + try { + const updated = await api.updatePool({ strategy }) + onChange(updated) + } finally { + setSaving(false) + } + } + + const regenKey = async () => { + setSaving(true) + try { + const updated = await api.regeneratePoolKey() + onChange(updated) + } finally { + setSaving(false) + } + } + + const copyKey = () => { + void navigator.clipboard.writeText(pool.apiKey) + setCopied(true) + setTimeout(() => setCopied(false), 1500) + } + + const maskedKey = + pool.apiKey?.length > 8 + ? `${pool.apiKey.slice(0, 8)}${"•".repeat(24)}` + : pool.apiKey ?? "" + + const proxyBase = `${window.location.protocol}//${window.location.hostname}:${proxyPort}` + + return ( +
+
+
+
Pool Mode
+
+ {pool.enabled + ? "Requests with pool key are load-balanced across running accounts" + : "Enable to auto-distribute requests across accounts"} +
+
+ +
+ {pool.enabled && ( + <> +
+ {(["round-robin", "priority"] as const).map((s) => ( + + ))} + + {pool.strategy === "round-robin" + ? "Evenly distribute across accounts" + : "Prefer higher-priority accounts first"} + +
+
+ + {copied ? "Copied!" : "Pool Key:"} + + + {keyVisible ? pool.apiKey : maskedKey} + + + +
+
+ Base URL: {proxyBase}  ·  Bearer {pool.apiKey?.slice(0, 8)}... +
+ + )} +
+ ) +} + function Dashboard() { const [accounts, setAccounts] = useState>([]) const [showForm, setShowForm] = useState(false) const [loading, setLoading] = useState(true) const [proxyPort, setProxyPort] = useState(4141) + const [pool, setPool] = useState({ + enabled: false, + strategy: "round-robin", + }) const refresh = useCallback(async () => { try { @@ -202,6 +382,7 @@ function Dashboard() { useEffect(() => { void api.getConfig().then((cfg) => setProxyPort(cfg.proxyPort)) + void api.getPool().then(setPool).catch(() => {}) void refresh() const interval = setInterval(() => void refresh(), 5000) return () => clearInterval(interval) @@ -241,6 +422,8 @@ function Dashboard() {
+ + {showForm && ( ) => + request(`/accounts/${id}`, { + method: "PUT", + body: JSON.stringify(data), + }), + + getPool: () => request("/pool"), + + updatePool: (data: Partial) => + request("/pool", { + method: "PUT", + body: JSON.stringify(data), + }), + + regeneratePoolKey: () => + request("/pool/regenerate-key", { method: "POST" }), } diff --git a/web/src/components/AccountCard.tsx b/web/src/components/AccountCard.tsx index da7ba8038..2d8d81c7e 100644 --- a/web/src/components/AccountCard.tsx +++ b/web/src/components/AccountCard.tsx @@ -343,6 +343,10 @@ export function AccountCard({ account, proxyPort, onRefresh }: Props) { const [usage, setUsage] = useState(null) const [usageLoading, setUsageLoading] = useState(false) const [showUsage, setShowUsage] = useState(false) + const [editingPriority, setEditingPriority] = useState(false) + const [priorityValue, setPriorityValue] = useState( + String(account.priority ?? 0), + ) const handleToggleUsage = async () => { if (showUsage) { @@ -373,6 +377,27 @@ export function AccountCard({ account, proxyPort, onRefresh }: Props) { })() } + const handlePrioritySave = () => { + const num = parseInt(priorityValue, 10) + if (isNaN(num) || num < 0) { + setPriorityValue(String(account.priority ?? 0)) + setEditingPriority(false) + return + } + setEditingPriority(false) + if (num !== (account.priority ?? 0)) { + void (async () => { + try { + await api.updateAccount(account.id, { priority: num }) + await onRefresh() + } catch (err) { + console.error("Priority update failed:", err) + setPriorityValue(String(account.priority ?? 0)) + } + })() + } + } + return (
+
+ Priority: + {editingPriority ? ( + setPriorityValue(e.target.value)} + onBlur={handlePrioritySave} + onKeyDown={(e) => { + if (e.key === "Enter") handlePrioritySave() + if (e.key === "Escape") { + setPriorityValue(String(account.priority ?? 0)) + setEditingPriority(false) + } + }} + autoFocus + min={0} + style={{ + width: 60, + padding: "2px 6px", + fontSize: 13, + display: "inline-block", + }} + /> + ) : ( + setEditingPriority(true)} + style={{ + cursor: "pointer", + padding: "2px 10px", + background: "var(--bg)", + border: "1px solid var(--border)", + borderRadius: 4, + fontFamily: "monospace", + fontSize: 13, + }} + title="Click to edit" + > + {account.priority ?? 0} + + )} + + Higher value = higher priority + +
+ {status === "running" && ( From c361511f73c5872b72fdd8fecb79fd406bcc1827 Mon Sep 17 00:00:00 2001 From: haruka <1628615876@qq.com> Date: Mon, 16 Feb 2026 18:30:55 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E8=BD=AE=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1265220ef..05cb8abcf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,4 +22,4 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file From 486bab857af078b10ddf58e3d1e5a3c113efbef2 Mon Sep 17 00:00:00 2001 From: Elysia <1628615876@qq.com> Date: Mon, 16 Feb 2026 23:01:02 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 55 ++++++ Dockerfile | 36 ++-- src/console/api.ts | 30 +++ src/console/index.ts | 5 +- web/src/App.tsx | 253 +++++++++++++++++++++---- web/src/api.ts | 9 + web/src/components/AccountCard.tsx | 46 ++--- web/src/components/AddAccountForm.tsx | 42 +++-- web/src/i18n.tsx | 256 ++++++++++++++++++++++++++ web/src/main.tsx | 5 +- 10 files changed, 642 insertions(+), 95 deletions(-) create mode 100644 CLAUDE.md create mode 100644 web/src/i18n.tsx diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..84aa310cd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,55 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +copilot-api is a reverse proxy that exposes GitHub Copilot as OpenAI and Anthropic-compatible API endpoints. Built with Bun, TypeScript, Hono, and citty (CLI framework). + +## Commands + +- **Build:** `bun run build` (tsdown bundler, outputs to `dist/`) +- **Dev:** `bun run dev` (watch mode via `bun run --watch`) +- **Start:** `bun run start` (production mode) +- **Lint:** `bun run lint` (ESLint with `@echristian/eslint-config`) +- **Typecheck:** `bun run typecheck` (tsc, no emit) +- **Test all:** `bun test` +- **Test single file:** `bun test tests/.test.ts` +- **Unused code detection:** `bun run knip` + +## Architecture + +### CLI Layer (`src/main.ts`) +Entry point uses citty to define subcommands: `start`, `auth`, `check-usage`, `debug`, `console`. + +### Server (`src/server.ts`) +Hono app with routes mounted at both `/` and `/v1/` prefixes for compatibility: +- `POST /v1/chat/completions` — OpenAI-compatible chat completions +- `POST /v1/messages` — Anthropic-compatible messages API +- `POST /v1/messages/count_tokens` — Token counting +- `GET /v1/models` — Model listing +- `POST /v1/embeddings` — Embeddings +- `GET /usage`, `GET /token` — Monitoring endpoints + +### Key Directories +- `src/routes/` — Route handlers, each in its own directory with `route.ts` + `handler.ts` +- `src/services/copilot/` — GitHub Copilot API calls (completions, embeddings, models) +- `src/services/github/` — GitHub API calls (auth, tokens, usage) +- `src/lib/` — Shared utilities (state, tokens, rate limiting, error handling, proxy) +- `src/console/` — Multi-account management mode with load balancing and web UI +- `web/` — React + Vite frontend for the console mode +- `tests/` — Bun test runner, files named `*.test.ts` + +### Global State (`src/lib/state.ts`) +Mutable singleton `state` object holds runtime config: GitHub/Copilot tokens, account type, cached models, rate limit settings. + +### Anthropic Translation Layer (`src/routes/messages/`) +Converts between Anthropic message format and Copilot's OpenAI-style API. Handles both streaming (`stream-translation.ts`) and non-streaming (`non-stream-translation.ts`) responses. + +## Code Conventions + +- ESM only, strict TypeScript — no `any`, no unused variables/imports +- Path alias: `~/*` maps to `src/*` (e.g., `import { state } from "~/lib/state"`) +- camelCase for variables/functions, PascalCase for types/classes +- Zod v4 for runtime validation +- Pre-commit hook runs lint-staged via simple-git-hooks \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 05cb8abcf..45182e161 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,39 @@ +# Stage 1: Build frontend +FROM node:22-alpine AS web-builder +WORKDIR /app/web + +COPY web/package.json ./ +RUN npm install + +COPY web/ ./ +RUN npm run build + +# Stage 2: Build backend FROM oven/bun:1.2.19-alpine AS builder WORKDIR /app -COPY ./package.json ./bun.lock ./ +COPY package.json bun.lock ./ RUN bun install --frozen-lockfile COPY . . -RUN bun run build -FROM oven/bun:1.2.19-alpine AS runner +# Stage 3: Runtime +FROM oven/bun:1.2.19-alpine WORKDIR /app -COPY ./package.json ./bun.lock ./ +COPY package.json bun.lock ./ RUN bun install --frozen-lockfile --production --ignore-scripts --no-cache -COPY --from=builder /app/dist ./dist +COPY --from=builder /app/src ./src +COPY --from=builder /app/tsconfig.json ./ +COPY --from=web-builder /app/web/dist ./web/dist + +EXPOSE 3000 4141 -EXPOSE 4141 +VOLUME /root/.local/share/copilot-api -HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ - CMD wget --spider -q http://localhost:4141/ || exit 1 +HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ + CMD wget --spider -q http://localhost:3000/api/config || exit 1 -COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["bun", "run", "./src/main.ts", "console"] +CMD ["--web-port", "3000", "--proxy-port", "4141"] diff --git a/src/console/api.ts b/src/console/api.ts index 4a2ce3746..bba8c6011 100644 --- a/src/console/api.ts +++ b/src/console/api.ts @@ -122,6 +122,36 @@ consoleApi.get("/accounts", async (c) => { return c.json(result) }) +// Batch usage for all running accounts +consoleApi.get("/accounts/usage", async (c) => { + const accounts = await getAccounts() + const results = await Promise.all( + accounts.map(async (account) => { + const status = getInstanceStatus(account.id) + if (status !== "running") { + return { + accountId: account.id, + name: account.name, + status, + usage: null, + } + } + try { + const usage = await getInstanceUsage(account.id) + return { accountId: account.id, name: account.name, status, usage } + } catch { + return { + accountId: account.id, + name: account.name, + status, + usage: null, + } + } + }), + ) + return c.json(results) +}) + // Get single account consoleApi.get("/accounts/:id", async (c) => { const account = await getAccount(c.req.param("id")) diff --git a/src/console/index.ts b/src/console/index.ts index 76178d98d..63b118fdc 100644 --- a/src/console/index.ts +++ b/src/console/index.ts @@ -128,10 +128,7 @@ function mountProxyRoutes(app: Hono): void { } async function mountStaticFiles(app: Hono): Promise { - const webDistPath = path.resolve( - new URL(".", import.meta.url).pathname, - "../../web/dist", - ) + const webDistPath = path.resolve(import.meta.dirname, "../../web/dist") let hasWebDist = false try { diff --git a/web/src/App.tsx b/web/src/App.tsx index b6df82c7c..11a5e3faf 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,27 +1,41 @@ import { useCallback, useEffect, useState } from "react" -import { api, getSessionToken, setSessionToken, type Account, type PoolConfig } from "./api" +import { api, getSessionToken, setSessionToken, type Account, type BatchUsageItem, type PoolConfig } from "./api" import { AccountCard } from "./components/AccountCard" import { AddAccountForm } from "./components/AddAccountForm" +import { useLocale, useT } from "./i18n" type AuthState = "loading" | "setup" | "login" | "authed" +function LanguageSwitcher() { + const { locale, setLocale } = useLocale() + return ( + + ) +} + function SetupForm({ onComplete }: { onComplete: () => void }) { const [username, setUsername] = useState("") const [password, setPassword] = useState("") const [confirm, setConfirm] = useState("") const [error, setError] = useState("") const [loading, setLoading] = useState(false) + const t = useT() const handleSubmit = async (e: React.SyntheticEvent) => { e.preventDefault() setError("") if (password !== confirm) { - setError("Passwords do not match") + setError(t("passwordMismatch")) return } if (password.length < 6) { - setError("Password must be at least 6 characters") + setError(t("passwordTooShort")) return } setLoading(true) @@ -38,18 +52,21 @@ function SetupForm({ onComplete }: { onComplete: () => void }) { return (
-

- Copilot API Console -

+
+

+ {t("consoleTitle")} +

+ +

- Create your admin account to get started + {t("setupSubtitle")}

void handleSubmit(e)}> setUsername(e.target.value)} - placeholder="Username" + placeholder={t("usernamePlaceholder")} autoFocus autoComplete="username" style={{ marginBottom: 12 }} @@ -58,7 +75,7 @@ function SetupForm({ onComplete }: { onComplete: () => void }) { type="password" value={password} onChange={(e) => setPassword(e.target.value)} - placeholder="Password (min 6 chars)" + placeholder={t("passwordPlaceholder")} autoComplete="new-password" style={{ marginBottom: 12 }} /> @@ -66,7 +83,7 @@ function SetupForm({ onComplete }: { onComplete: () => void }) { type="password" value={confirm} onChange={(e) => setConfirm(e.target.value)} - placeholder="Confirm password" + placeholder={t("confirmPasswordPlaceholder")} autoComplete="new-password" style={{ marginBottom: 12 }} /> @@ -76,7 +93,7 @@ function SetupForm({ onComplete }: { onComplete: () => void }) {
)} @@ -88,6 +105,7 @@ function LoginForm({ onLogin }: { onLogin: () => void }) { const [password, setPassword] = useState("") const [error, setError] = useState("") const [loading, setLoading] = useState(false) + const t = useT() const handleSubmit = async (e: React.SyntheticEvent) => { e.preventDefault() @@ -98,7 +116,7 @@ function LoginForm({ onLogin }: { onLogin: () => void }) { setSessionToken(token) onLogin() } catch { - setError("Invalid username or password") + setError(t("invalidCredentials")) } finally { setLoading(false) } @@ -106,18 +124,21 @@ function LoginForm({ onLogin }: { onLogin: () => void }) { return (
-

- Copilot API Console -

+
+

+ {t("consoleTitle")} +

+ +

- Sign in to continue + {t("loginSubtitle")}

void handleSubmit(e)}> setUsername(e.target.value)} - placeholder="Username" + placeholder={t("usernamePlaceholder")} autoFocus autoComplete="username" style={{ marginBottom: 12 }} @@ -126,7 +147,7 @@ function LoginForm({ onLogin }: { onLogin: () => void }) { type="password" value={password} onChange={(e) => setPassword(e.target.value)} - placeholder="Password" + placeholder={t("passwordPlaceholder")} autoComplete="current-password" style={{ marginBottom: 12 }} /> @@ -136,7 +157,7 @@ function LoginForm({ onLogin }: { onLogin: () => void }) {
)} @@ -152,6 +173,8 @@ function AccountList({ proxyPort: number onRefresh: () => Promise }) { + const t = useT() + if (accounts.length === 0) { return (
-

No accounts configured

-

Add a GitHub account to get started

+

{t("noAccounts")}

+

{t("noAccountsHint")}

) } @@ -195,6 +218,7 @@ function PoolSettings({ const [saving, setSaving] = useState(false) const [keyVisible, setKeyVisible] = useState(false) const [copied, setCopied] = useState(false) + const t = useT() const toggle = async () => { setSaving(true) @@ -257,11 +281,11 @@ function PoolSettings({ }} >
-
Pool Mode
+
{t("poolMode")}
{pool.enabled - ? "Requests with pool key are load-balanced across running accounts" - : "Enable to auto-distribute requests across accounts"} + ? t("poolEnabledDesc") + : t("poolDisabledDesc")}
{pool.enabled && ( @@ -284,7 +308,7 @@ function PoolSettings({ disabled={saving || pool.strategy === s} style={{ fontSize: 13 }} > - {s === "round-robin" ? "Round Robin" : "Priority"} + {s === "round-robin" ? t("roundRobin") : t("priority")} ))} {pool.strategy === "round-robin" - ? "Evenly distribute across accounts" - : "Prefer higher-priority accounts first"} + ? t("roundRobinDesc") + : t("priorityDesc")}
- {copied ? "Copied!" : "Pool Key:"} + {copied ? t("copied") : t("poolKey")} setKeyVisible(!keyVisible)} style={{ padding: "2px 8px", fontSize: 11 }} > - {keyVisible ? "Hide" : "Show"} + {keyVisible ? t("hide") : t("show")}
- Base URL: {proxyBase}  ·  Bearer {pool.apiKey?.slice(0, 8)}... + {t("baseUrl")} {proxyBase}  ·  Bearer {pool.apiKey?.slice(0, 8)}...
)} @@ -359,6 +383,154 @@ function PoolSettings({ ) } +function usageColor(pct: number): string { + if (pct > 90) return "var(--red)" + if (pct > 70) return "var(--yellow)" + return "var(--green)" +} + +function UsageCell({ used, total }: { used: number; total: number }) { + const pct = total > 0 ? (used / total) * 100 : 0 + return ( + + {used} + / {total} + + ) +} + +function BatchUsagePanel() { + const [items, setItems] = useState>([]) + const [loading, setLoading] = useState(false) + const [open, setOpen] = useState(false) + const [fetched, setFetched] = useState(false) + const t = useT() + + const fetchAll = async () => { + setLoading(true) + try { + const data = await api.getAllUsage() + setItems(data) + setFetched(true) + setOpen(true) + } catch (err) { + console.error("Batch usage failed:", err) + } finally { + setLoading(false) + } + } + + const runningItems = items.filter((i) => i.usage) + + const totals = runningItems.reduce( + (acc, i) => { + const q = i.usage!.quota_snapshots + acc.premiumUsed += q.premium_interactions.entitlement - q.premium_interactions.remaining + acc.premiumTotal += q.premium_interactions.entitlement + acc.chatUsed += q.chat.entitlement - q.chat.remaining + acc.chatTotal += q.chat.entitlement + acc.compUsed += q.completions.entitlement - q.completions.remaining + acc.compTotal += q.completions.entitlement + return acc + }, + { premiumUsed: 0, premiumTotal: 0, chatUsed: 0, chatTotal: 0, compUsed: 0, compTotal: 0 }, + ) + + const thStyle: React.CSSProperties = { + padding: "8px 10px", + textAlign: "left", + fontSize: 12, + fontWeight: 600, + color: "var(--text-muted)", + borderBottom: "1px solid var(--border)", + } + + return ( +
+
+
{t("batchUsage")}
+
+ + {fetched && ( + + )} +
+
+ + {open && fetched && ( +
+ {runningItems.length === 0 ? ( +
+ {t("noRunningAccounts")} +
+ ) : ( + + + + + + + + + + + + + {runningItems.map((item) => { + const q = item.usage!.quota_snapshots + return ( + + + + + + + + + ) + })} + + + + +
{t("colAccount")}{t("colPlan")}{t("colPremium")}{t("colChat")}{t("colCompletions")}{t("colResets")}
{item.name} + {item.usage!.copilot_plan} + + {item.usage!.quota_reset_date} +
{t("totalSummary")} + + + + +
+ )} +
+ )} +
+ ) +} + function Dashboard() { const [accounts, setAccounts] = useState>([]) const [showForm, setShowForm] = useState(false) @@ -368,6 +540,7 @@ function Dashboard() { enabled: false, strategy: "round-robin", }) + const t = useT() const refresh = useCallback(async () => { try { @@ -409,21 +582,24 @@ function Dashboard() { }} >
-

Copilot API Console

+

{t("consoleTitle")}

- Manage multiple GitHub Copilot proxy accounts + {t("dashboardSubtitle")}

+ - +
+ + {showForm && ( - Loading... + {t("loading")}

: ("loading") + const t = useT() useEffect(() => { void (async () => { @@ -488,7 +665,7 @@ export function App() { padding: 120, }} > - Loading... + {t("loading")} ) } diff --git a/web/src/api.ts b/web/src/api.ts index 2a3aa9830..84e155327 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -72,6 +72,13 @@ export interface ConfigResponse { needsSetup: boolean } +export interface BatchUsageItem { + accountId: string + name: string + status: string + usage: UsageData | null +} + interface ErrorBody { error?: string } @@ -123,6 +130,8 @@ export const api = { getUsage: (id: string) => request(`/accounts/${id}/usage`), + getAllUsage: () => request>("/accounts/usage"), + regenerateKey: (id: string) => request(`/accounts/${id}/regenerate-key`, { method: "POST" }), diff --git a/web/src/components/AccountCard.tsx b/web/src/components/AccountCard.tsx index 2d8d81c7e..ec6df6cee 100644 --- a/web/src/components/AccountCard.tsx +++ b/web/src/components/AccountCard.tsx @@ -1,6 +1,7 @@ import { useState } from "react" import { api, type Account, type UsageData } from "../api" +import { useT } from "../i18n" function StatusBadge({ status }: { status: string }) { const colorMap: Record = { @@ -86,6 +87,7 @@ function QuotaBar({ function UsagePanel({ usage }: { usage: UsageData }) { const q = usage.quota_snapshots + const t = useT() return (
- Plan: {usage.copilot_plan} · Resets: {usage.quota_reset_date} + {t("plan")} {usage.copilot_plan} · {t("resets")} {usage.quota_reset_date}
@@ -140,6 +142,7 @@ function ApiKeyPanel({ }) { const [visible, setVisible] = useState(false) const [copied, copy] = useCopyFeedback() + const t = useT() const safeKey = apiKey ?? "" const masked = safeKey.length > 8 ? `${safeKey.slice(0, 8)}${"•".repeat(24)}` : safeKey const isCopied = copied === safeKey @@ -159,7 +162,7 @@ function ApiKeyPanel({ }} > - {isCopied ? "Copied!" : "API Key:"} + {isCopied ? t("copied") : t("apiKey")} copy(safeKey)} @@ -177,25 +180,19 @@ function ApiKeyPanel({ onClick={() => setVisible(!visible)} style={{ padding: "2px 8px", fontSize: 11 }} > - {visible ? "Hide" : "Show"} + {visible ? t("hide") : t("show")} ) } -function getUsageLabel(loading: boolean, visible: boolean): string { - if (loading) return "..." - if (visible) return "Hide Usage" - return "Usage" -} - interface Props { account: Account proxyPort: number @@ -219,6 +216,7 @@ function AccountActions({ }) { const [actionLoading, setActionLoading] = useState(false) const [confirmDelete, setConfirmDelete] = useState(false) + const t = useT() const handleAction = async (action: () => Promise) => { setActionLoading(true) @@ -246,7 +244,7 @@ function AccountActions({
{status === "running" && ( )} {status === "running" ? @@ -254,14 +252,14 @@ function AccountActions({ onClick={() => void handleAction(() => api.stopInstance(account.id))} disabled={actionLoading} > - Stop + {t("stop")} : }
) @@ -279,6 +277,7 @@ function EndpointsPanel({ apiKey, proxyPort }: { apiKey: string; proxyPort: numb const proxyBase = `${window.location.protocol}//${window.location.hostname}:${proxyPort}` const safeKey = apiKey ?? "YOUR_API_KEY" const [copied, copy] = useCopyFeedback() + const t = useT() const endpoints = [ { label: "OpenAI", path: "/v1/chat/completions" }, @@ -305,7 +304,7 @@ function EndpointsPanel({ apiKey, proxyPort }: { apiKey: string; proxyPort: numb justifyContent: "space-between", }} > - Endpoints (Bearer {safeKey.slice(0, 8)}...) + {t("endpoints")} (Bearer {safeKey.slice(0, 8)}...) {proxyBase}
@@ -329,7 +328,7 @@ function EndpointsPanel({ apiKey, proxyPort }: { apiKey: string; proxyPort: numb }} title={url} > - {isCopied ? "Copied!" : ep.label} + {isCopied ? t("copied") : ep.label} ) })} @@ -347,6 +346,7 @@ export function AccountCard({ account, proxyPort, onRefresh }: Props) { const [priorityValue, setPriorityValue] = useState( String(account.priority ?? 0), ) + const t = useT() const handleToggleUsage = async () => { if (showUsage) { @@ -434,7 +434,7 @@ export function AccountCard({ account, proxyPort, onRefresh }: Props) {
{account.error && (
- Error: {account.error} + {t("error")} {account.error}
)} @@ -458,7 +458,7 @@ export function AccountCard({ account, proxyPort, onRefresh }: Props) { fontSize: 13, }} > - Priority: + {t("priorityLabel")} {editingPriority ? ( )} - Higher value = higher priority + {t("priorityHint")} @@ -517,7 +517,7 @@ export function AccountCard({ account, proxyPort, onRefresh }: Props) { color: "var(--text-muted)", }} > - Usage data unavailable. Make sure the instance is running. + {t("usageUnavailable")} )} ) diff --git a/web/src/components/AddAccountForm.tsx b/web/src/components/AddAccountForm.tsx index 73723673c..4a057d656 100644 --- a/web/src/components/AddAccountForm.tsx +++ b/web/src/components/AddAccountForm.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useRef, useState } from "react" import { api } from "../api" +import { useT } from "../i18n" interface Props { onComplete: () => Promise @@ -16,6 +17,7 @@ function DeviceCodeDisplay({ userCode: string verificationUri: string }) { + const t = useT() return (

- Enter this code on GitHub: + {t("enterCode")}

void navigator.clipboard.writeText(userCode)} @@ -48,7 +50,7 @@ function DeviceCodeDisplay({ {userCode}

- Click the code to copy + {t("clickToCopy")}

- Open GitHub + {t("openGithub")}
) @@ -83,10 +85,11 @@ function AuthorizeStep({ error: string onCancel: () => void }) { + const t = useT() return (

- GitHub Authorization + {t("githubAuth")}

@@ -155,31 +158,32 @@ function ConfigForm({ accountType: string setAccountType: (v: string) => void }) { + const t = useT() return (

- Add Account + {t("addAccountTitle")}

- + setName(e.target.value)} - placeholder="e.g. Personal" + placeholder={t("accountNamePlaceholder")} />
- +
@@ -190,10 +194,10 @@ function ConfigForm({ )}
@@ -208,6 +212,7 @@ function useAuthFlow(onComplete: () => Promise) { const [loading, setLoading] = useState(false) const [error, setError] = useState("") const pollRef = useRef | null>(null) + const t = useT() const cleanup = useCallback(() => { if (pollRef.current) { @@ -226,7 +231,7 @@ function useAuthFlow(onComplete: () => Promise) { setUserCode(result.userCode) setVerificationUri(result.verificationUri) setStep("authorize") - setAuthStatus("Waiting for authorization...") + setAuthStatus(t("waitingAuth")) pollRef.current = setInterval(() => { void (async () => { @@ -234,7 +239,7 @@ function useAuthFlow(onComplete: () => Promise) { const poll = await api.pollAuth(result.sessionId) if (poll.status === "completed") { cleanup() - setAuthStatus("Authorized! Creating account...") + setAuthStatus(t("authorized")) await api.completeAuth({ sessionId: result.sessionId, name, @@ -245,7 +250,7 @@ function useAuthFlow(onComplete: () => Promise) { } else if (poll.status === "expired" || poll.status === "error") { cleanup() setAuthStatus("") - setError(poll.error ?? "Authorization failed or expired") + setError(poll.error ?? t("authFailed")) } } catch { // poll error, keep trying @@ -276,11 +281,12 @@ export function AddAccountForm({ onComplete, onCancel }: Props) { const [name, setName] = useState("") const [accountType, setAccountType] = useState("individual") const auth = useAuthFlow(onComplete) + const t = useT() const handleSubmit = (e: React.SyntheticEvent) => { e.preventDefault() if (!name.trim()) { - auth.setError("Account name is required") + auth.setError(t("accountNameRequired")) return } void auth.startAuth(name.trim(), accountType) diff --git a/web/src/i18n.tsx b/web/src/i18n.tsx new file mode 100644 index 000000000..b40714775 --- /dev/null +++ b/web/src/i18n.tsx @@ -0,0 +1,256 @@ +import { createContext, useCallback, useContext, useState, type ReactNode } from "react" + +export type Locale = "en" | "zh" + +const STORAGE_KEY = "copilot-api-locale" + +const en = { + // Common + loading: "Loading...", + cancel: "Cancel", + copied: "Copied!", + hide: "Hide", + show: "Show", + regen: "Regen", + + // Auth + consoleTitle: "Copilot API Console", + setupSubtitle: "Create your admin account to get started", + loginSubtitle: "Sign in to continue", + usernamePlaceholder: "Username", + passwordPlaceholder: "Password (min 6 chars)", + confirmPasswordPlaceholder: "Confirm password", + passwordMismatch: "Passwords do not match", + passwordTooShort: "Password must be at least 6 characters", + creating: "Creating...", + createAdmin: "Create Admin Account", + signingIn: "Signing in...", + signIn: "Sign In", + invalidCredentials: "Invalid username or password", + logout: "Logout", + + // Dashboard + dashboardSubtitle: "Manage multiple GitHub Copilot proxy accounts", + addAccount: "+ Add Account", + noAccounts: "No accounts configured", + noAccountsHint: "Add a GitHub account to get started", + + // Pool + poolMode: "Pool Mode", + poolEnabledDesc: "Requests with pool key are load-balanced across running accounts", + poolDisabledDesc: "Enable to auto-distribute requests across accounts", + disable: "Disable", + enable: "Enable", + roundRobin: "Round Robin", + priority: "Priority", + roundRobinDesc: "Evenly distribute across accounts", + priorityDesc: "Prefer higher-priority accounts first", + poolKey: "Pool Key:", + baseUrl: "Base URL:", + + // Account Card + apiKey: "API Key:", + endpoints: "Endpoints", + priorityLabel: "Priority:", + priorityHint: "Higher value = higher priority", + usageUnavailable: "Usage data unavailable. Make sure the instance is running.", + usage: "Usage", + hideUsage: "Hide Usage", + stop: "Stop", + start: "Start", + starting: "Starting...", + delete: "Delete", + confirmDelete: "Confirm?", + plan: "Plan:", + resets: "Resets:", + premium: "Premium", + chat: "Chat", + completions: "Completions", + error: "Error:", + + // Add Account + addAccountTitle: "Add Account", + accountName: "Account Name", + accountNamePlaceholder: "e.g. Personal", + accountType: "Account Type", + individual: "Individual", + business: "Business", + enterprise: "Enterprise", + loginWithGithub: "Login with GitHub", + accountNameRequired: "Account name is required", + + // GitHub Auth + githubAuth: "GitHub Authorization", + enterCode: "Enter this code on GitHub:", + clickToCopy: "Click the code to copy", + openGithub: "Open GitHub", + waitingAuth: "Waiting for authorization...", + authorized: "Authorized! Creating account...", + authFailed: "Authorization failed or expired", + + // Batch Usage + batchUsage: "Batch Usage Query", + queryAllUsage: "Query All Usage", + refreshing: "Querying...", + colAccount: "Account", + colStatus: "Status", + colPlan: "Plan", + colPremium: "Premium", + colChat: "Chat", + colCompletions: "Completions", + colResets: "Resets", + totalSummary: "Total", + noRunningAccounts: "No running accounts", +} as const + +export type TranslationKey = keyof typeof en +type Translations = Record + +const zh: Translations = { + // Common + loading: "加载中...", + cancel: "取消", + copied: "已复制!", + hide: "隐藏", + show: "显示", + regen: "重新生成", + + // Auth + consoleTitle: "Copilot API 控制台", + setupSubtitle: "创建管理员账户以开始使用", + loginSubtitle: "登录以继续", + usernamePlaceholder: "用户名", + passwordPlaceholder: "密码(至少 6 位)", + confirmPasswordPlaceholder: "确认密码", + passwordMismatch: "两次输入的密码不一致", + passwordTooShort: "密码至少需要 6 个字符", + creating: "创建中...", + createAdmin: "创建管理员账户", + signingIn: "登录中...", + signIn: "登录", + invalidCredentials: "用户名或密码错误", + logout: "退出登录", + + // Dashboard + dashboardSubtitle: "管理多个 GitHub Copilot 代理账户", + addAccount: "+ 添加账户", + noAccounts: "暂无账户", + noAccountsHint: "添加一个 GitHub 账户以开始使用", + + // Pool + poolMode: "池模式", + poolEnabledDesc: "使用池密钥的请求将在运行中的账户间负载均衡", + poolDisabledDesc: "启用后可自动分配请求到各账户", + disable: "禁用", + enable: "启用", + roundRobin: "轮询", + priority: "优先级", + roundRobinDesc: "均匀分配到各账户", + priorityDesc: "优先使用高优先级账户", + poolKey: "池密钥:", + baseUrl: "基础 URL:", + + // Account Card + apiKey: "API 密钥:", + endpoints: "接口端点", + priorityLabel: "优先级:", + priorityHint: "数值越大优先级越高", + usageUnavailable: "用量数据不可用,请确保实例正在运行。", + usage: "用量", + hideUsage: "隐藏用量", + stop: "停止", + start: "启动", + starting: "启动中...", + delete: "删除", + confirmDelete: "确认?", + plan: "计划:", + resets: "重置:", + premium: "高级", + chat: "对话", + completions: "补全", + error: "错误:", + + // Add Account + addAccountTitle: "添加账户", + accountName: "账户名称", + accountNamePlaceholder: "例如:个人", + accountType: "账户类型", + individual: "个人", + business: "商业", + enterprise: "企业", + loginWithGithub: "使用 GitHub 登录", + accountNameRequired: "请输入账户名称", + + // GitHub Auth + githubAuth: "GitHub 授权", + enterCode: "在 GitHub 上输入此代码:", + clickToCopy: "点击代码即可复制", + openGithub: "打开 GitHub", + waitingAuth: "等待授权中...", + authorized: "已授权!正在创建账户...", + authFailed: "授权失败或已过期", + + // Batch Usage + batchUsage: "批量额度查询", + queryAllUsage: "查询所有额度", + refreshing: "查询中...", + colAccount: "账户", + colStatus: "状态", + colPlan: "计划", + colPremium: "高级", + colChat: "对话", + colCompletions: "补全", + colResets: "重置日期", + totalSummary: "合计", + noRunningAccounts: "暂无运行中的账户", +} as const + +interface I18nContextValue { + locale: Locale + setLocale: (locale: Locale) => void + t: (key: TranslationKey) => string +} + +const I18nContext = createContext(null) + +function getInitialLocale(): Locale { + const saved = localStorage.getItem(STORAGE_KEY) + if (saved === "en" || saved === "zh") return saved + const lang = navigator.language + if (lang.startsWith("zh")) return "zh" + return "en" +} + +const translations: Record = { en, zh } + +export function I18nProvider({ children }: { children: ReactNode }) { + const [locale, setLocaleState] = useState(getInitialLocale) + + const setLocale = useCallback((l: Locale) => { + setLocaleState(l) + localStorage.setItem(STORAGE_KEY, l) + }, []) + + const t = useCallback( + (key: TranslationKey) => translations[locale][key], + [locale], + ) + + return ( + + {children} + + ) +} + +export function useT() { + const ctx = useContext(I18nContext) + if (!ctx) throw new Error("useT must be used within I18nProvider") + return ctx.t +} + +export function useLocale() { + const ctx = useContext(I18nContext) + if (!ctx) throw new Error("useLocale must be used within I18nProvider") + return { locale: ctx.locale, setLocale: ctx.setLocale } +} diff --git a/web/src/main.tsx b/web/src/main.tsx index c047c907c..30a6164b5 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -2,13 +2,16 @@ import { StrictMode } from "react" import { createRoot } from "react-dom/client" import { App } from "./App" +import { I18nProvider } from "./i18n" import "./index.css" const root = document.querySelector("#root") if (root) { createRoot(root).render( - + + + , ) } From 7f07915912e98d68bd33fcf60a04e69740a0847d Mon Sep 17 00:00:00 2001 From: Elysia <1628615876@qq.com> Date: Tue, 17 Feb 2026 10:15:14 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..6f3cf7c0b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +services: + copilot-api: + image: copilot-api + ports: + - "3000:3000" + - "4141:4141" + volumes: + - copilot-api-data:/root/.local/share/copilot-api + restart: unless-stopped + +volumes: + copilot-api-data: From 79985e75e080e1e2699aa2881c727b8d5639c2b0 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 27 Feb 2026 11:42:49 +0800 Subject: [PATCH 08/13] fix: model name translation and HTTP error body handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - translateModelName: strip [1m]/bracket suffixes, 8-digit date stamps, and convert hyphen minor versions to dot notation (e.g. claude-haiku-4-5 → claude-haiku-4.5) to match Copilot's expected model ID format - HTTPError: carry pre-read response body to avoid "Body already read" crash - forwardError: use stored body instead of re-reading response; remove async - get-models: pre-read body before throwing HTTPError - route.ts: remove unnecessary optional chain on non-nullable supports/limits Co-Authored-By: Claude Opus 4.6 --- src/lib/api-config.ts | 2 +- src/lib/error.ts | 8 +- src/routes/messages/anthropic-types.ts | 2 +- src/routes/messages/handler.ts | 52 ++++++++-- src/routes/messages/non-stream-translation.ts | 97 +++++++++++++++++-- src/routes/messages/stream-translation.ts | 12 +-- src/routes/models/route.ts | 35 +++++-- .../copilot/create-chat-completions.ts | 87 +++++++++++++++-- src/services/copilot/get-models.ts | 5 +- src/services/get-vscode-version.ts | 2 +- 10 files changed, 255 insertions(+), 47 deletions(-) diff --git a/src/lib/api-config.ts b/src/lib/api-config.ts index 83bce92ad..54d5ff0fd 100644 --- a/src/lib/api-config.ts +++ b/src/lib/api-config.ts @@ -7,7 +7,7 @@ export const standardHeaders = () => ({ accept: "application/json", }) -const COPILOT_VERSION = "0.26.7" +const COPILOT_VERSION = "0.38.2026022603" const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}` const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}` diff --git a/src/lib/error.ts b/src/lib/error.ts index c39c22596..1a084fe1f 100644 --- a/src/lib/error.ts +++ b/src/lib/error.ts @@ -5,18 +5,20 @@ import consola from "consola" export class HTTPError extends Error { response: Response + body: string - constructor(message: string, response: Response) { + constructor(message: string, response: Response, body: string) { super(message) this.response = response + this.body = body } } -export async function forwardError(c: Context, error: unknown) { +export function forwardError(c: Context, error: unknown) { consola.error("Error occurred:", error) if (error instanceof HTTPError) { - const errorText = await error.response.text() + const errorText = error.body let errorJson: unknown try { errorJson = JSON.parse(errorText) diff --git a/src/routes/messages/anthropic-types.ts b/src/routes/messages/anthropic-types.ts index 881fffcc8..4355ed24a 100644 --- a/src/routes/messages/anthropic-types.ts +++ b/src/routes/messages/anthropic-types.ts @@ -42,7 +42,7 @@ export interface AnthropicImageBlock { export interface AnthropicToolResultBlock { type: "tool_result" tool_use_id: string - content: string + content: string | Array is_error?: boolean } diff --git a/src/routes/messages/handler.ts b/src/routes/messages/handler.ts index 85dbf6243..13e98677e 100644 --- a/src/routes/messages/handler.ts +++ b/src/routes/messages/handler.ts @@ -6,6 +6,7 @@ import { streamSSE } from "hono/streaming" import { awaitApproval } from "~/lib/approval" import { checkRateLimit } from "~/lib/rate-limit" import { state } from "~/lib/state" +import { isNullish } from "~/lib/utils" import { createChatCompletions, type ChatCompletionChunk, @@ -20,7 +21,10 @@ import { translateToAnthropic, translateToOpenAI, } from "./non-stream-translation" -import { translateChunkToAnthropicEvents } from "./stream-translation" +import { + translateChunkToAnthropicEvents, + translateErrorToAnthropicErrorEvent, +} from "./stream-translation" export async function handleCompletion(c: Context) { await checkRateLimit(state) @@ -28,7 +32,22 @@ export async function handleCompletion(c: Context) { const anthropicPayload = await c.req.json() consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload)) - const openAIPayload = translateToOpenAI(anthropicPayload) + let openAIPayload = translateToOpenAI(anthropicPayload) + + // Find the selected model for limits lookup + const selectedModel = state.models?.data.find( + (model) => model.id === openAIPayload.model, + ) + + // Fill in max_tokens if not provided, using model's max output tokens as default + if (isNullish(openAIPayload.max_tokens)) { + openAIPayload = { + ...openAIPayload, + max_tokens: selectedModel?.capabilities.limits.max_output_tokens, + } + consola.debug("Set max_tokens to:", openAIPayload.max_tokens) + } + consola.debug( "Translated OpenAI request payload:", JSON.stringify(openAIPayload), @@ -62,6 +81,12 @@ export async function handleCompletion(c: Context) { toolCalls: {}, } + // Anthropic SDK expects a ping event at the start of the stream + await stream.writeSSE({ + event: "ping", + data: JSON.stringify({ type: "ping" }), + }) + for await (const rawEvent of response) { consola.debug("Copilot raw stream event:", JSON.stringify(rawEvent)) if (rawEvent.data === "[DONE]") { @@ -72,15 +97,26 @@ export async function handleCompletion(c: Context) { continue } - const chunk = JSON.parse(rawEvent.data) as ChatCompletionChunk - const events = translateChunkToAnthropicEvents(chunk, streamState) + try { + const chunk = JSON.parse(rawEvent.data) as ChatCompletionChunk + const events = translateChunkToAnthropicEvents(chunk, streamState) - for (const event of events) { - consola.debug("Translated Anthropic event:", JSON.stringify(event)) + for (const event of events) { + consola.debug("Translated Anthropic event:", JSON.stringify(event)) + await stream.writeSSE({ + event: event.type, + data: JSON.stringify(event), + }) + } + } catch (err) { + consola.error("Error processing stream chunk:", err) + // Send an Anthropic-formatted error event so clients don't hang + const errorEvent = translateErrorToAnthropicErrorEvent() await stream.writeSSE({ - event: event.type, - data: JSON.stringify(event), + event: errorEvent.type, + data: JSON.stringify(errorEvent), }) + break } } }) diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index dc41e6382..3fd20f5f6 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -47,13 +47,42 @@ export function translateToOpenAI( } function translateModelName(model: string): string { - // Subagent requests use a specific model number which Copilot doesn't support - if (model.startsWith("claude-sonnet-4-")) { - return model.replace(/^claude-sonnet-4-.*/, "claude-sonnet-4") - } else if (model.startsWith("claude-opus-")) { - return model.replace(/^claude-opus-4-.*/, "claude-opus-4") + // Claude Code appends various suffixes that Copilot doesn't recognize. + // Strip them to get the base model ID that Copilot accepts. + // + // Known suffixes: + // "[1m]" — extended thinking / 1M token context variant + // e.g. "claude-opus-4.6[1m]" → "claude-opus-4.6" + // "-YYYYMMDD" — date-stamped model versions from Anthropic API + // e.g. "claude-sonnet-4-20250514" → "claude-sonnet-4" + // + // Copilot uses dot-notation for minor versions (e.g. "claude-haiku-4.5") + // but Claude Code sends hyphen-notation (e.g. "claude-haiku-4-5"). + // Convert the trailing "-N" minor version to ".N". + + let result = model + + // Strip bracket suffixes like [1m], [thinking], etc. + const bracketSuffixPattern = /\[.*\]$/ + if (bracketSuffixPattern.test(result)) { + result = result.replace(bracketSuffixPattern, "") } - return model + + // Strip 8-digit date suffixes like -20250514 + const dateSuffixPattern = /-\d{8}$/ + if (dateSuffixPattern.test(result)) { + result = result.replace(dateSuffixPattern, "") + } + + // Convert hyphen minor version to dot: "claude-haiku-4-5" → "claude-haiku-4.5" + // Matches: word-N-N at the end where the last segment is a short version number + const hyphenMinorVersionPattern = /^(.*-\d+)-(\d+)$/ + const match = hyphenMinorVersionPattern.exec(result) + if (match) { + result = `${match[1]}.${match[2]}` + } + + return result } function translateAnthropicMessagesToOpenAI( @@ -71,6 +100,25 @@ function translateAnthropicMessagesToOpenAI( return [...systemMessages, ...otherMessages] } +// Copilot rejects system prompts containing these reserved Anthropic-internal keywords. +// Claude Code injects them as metadata headers inside the system prompt. +const COPILOT_RESERVED_KEYWORDS = [ + "", + "x-anthropic-billing-header", +] + +function sanitizeSystemPrompt(text: string): string { + // Remove lines containing reserved Copilot keywords + const lines = text.split("\n") + const filtered = lines.filter( + (line) => + !COPILOT_RESERVED_KEYWORDS.some((kw) => + line.toLowerCase().includes(kw.toLowerCase()), + ), + ) + return filtered.join("\n").trim() +} + function handleSystemPrompt( system: string | Array | undefined, ): Array { @@ -79,10 +127,14 @@ function handleSystemPrompt( } if (typeof system === "string") { - return [{ role: "system", content: system }] + const content = sanitizeSystemPrompt(system) + if (!content) return [] + return [{ role: "system", content }] } else { const systemText = system.map((block) => block.text).join("\n\n") - return [{ role: "system", content: systemText }] + const content = sanitizeSystemPrompt(systemText) + if (!content) return [] + return [{ role: "system", content }] } } @@ -103,7 +155,7 @@ function handleUserMessage(message: AnthropicUserMessage): Array { newMessages.push({ role: "tool", tool_call_id: block.tool_use_id, - content: mapContent(block.content), + content: mapToolResultContent(block.content), }) } @@ -176,6 +228,33 @@ function handleAssistantMessage( ] } +function mapToolResultContent( + content: AnthropicToolResultBlock["content"], +): string | Array | null { + if (typeof content === "string") { + return content + } + // Array of text/image blocks — flatten to a string or content parts + const hasImage = content.some((block) => block.type === "image") + if (!hasImage) { + return content + .filter((block): block is AnthropicTextBlock => block.type === "text") + .map((block) => block.text) + .join("\n\n") + } + return content.map((block) => { + if (block.type === "text") { + return { type: "text" as const, text: block.text } + } + return { + type: "image_url" as const, + image_url: { + url: `data:${block.source.media_type};base64,${block.source.data}`, + }, + } + }) +} + function mapContent( content: | string diff --git a/src/routes/messages/stream-translation.ts b/src/routes/messages/stream-translation.ts index 55094448f..f80d275f1 100644 --- a/src/routes/messages/stream-translation.ts +++ b/src/routes/messages/stream-translation.ts @@ -42,10 +42,10 @@ export function translateChunkToAnthropicEvents( stop_reason: null, stop_sequence: null, usage: { - input_tokens: - (chunk.usage?.prompt_tokens ?? 0) - - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0), - output_tokens: 0, // Will be updated in message_delta when finished + // usage is not available in the first chunk for most providers; + // we use 0 as a placeholder and fill in accurate values in message_delta + input_tokens: chunk.usage?.prompt_tokens ?? 0, + output_tokens: 0, ...(chunk.usage?.prompt_tokens_details?.cached_tokens !== undefined && { cache_read_input_tokens: @@ -159,9 +159,7 @@ export function translateChunkToAnthropicEvents( stop_sequence: null, }, usage: { - input_tokens: - (chunk.usage?.prompt_tokens ?? 0) - - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0), + input_tokens: chunk.usage?.prompt_tokens ?? 0, output_tokens: chunk.usage?.completion_tokens ?? 0, ...(chunk.usage?.prompt_tokens_details?.cached_tokens !== undefined && { diff --git a/src/routes/models/route.ts b/src/routes/models/route.ts index 5254e2af7..1f54234b6 100644 --- a/src/routes/models/route.ts +++ b/src/routes/models/route.ts @@ -13,15 +13,30 @@ modelRoutes.get("/", async (c) => { await cacheModels() } - const models = state.models?.data.map((model) => ({ - id: model.id, - object: "model", - type: "model", - created: 0, // No date available from source - created_at: new Date(0).toISOString(), // No date available from source - owned_by: model.vendor, - display_name: model.name, - })) + // Only surface models that are enabled for user selection by default. + // Pass ?all=1 to see every model including internal/preview ones. + const showAll = c.req.query("all") === "1" + + const models = state.models?.data + .filter((model) => showAll || model.model_picker_enabled) + .map((model) => ({ + id: model.id, + object: "model", + type: "model", + created: 0, // No date available from source + created_at: new Date(0).toISOString(), // No date available from source + owned_by: model.vendor, + display_name: model.name, + preview: model.preview, + capabilities: { + supports_tool_calls: model.capabilities.supports.tool_calls ?? false, + supports_parallel_tool_calls: + model.capabilities.supports.parallel_tool_calls ?? false, + max_context_window_tokens: + model.capabilities.limits.max_context_window_tokens, + max_output_tokens: model.capabilities.limits.max_output_tokens, + }, + })) return c.json({ object: "list", @@ -29,6 +44,6 @@ modelRoutes.get("/", async (c) => { has_more: false, }) } catch (error) { - return await forwardError(c, error) + return forwardError(c, error) } }) diff --git a/src/services/copilot/create-chat-completions.ts b/src/services/copilot/create-chat-completions.ts index 8534151da..50114879c 100644 --- a/src/services/copilot/create-chat-completions.ts +++ b/src/services/copilot/create-chat-completions.ts @@ -5,12 +5,83 @@ import { copilotHeaders, copilotBaseUrl } from "~/lib/api-config" import { HTTPError } from "~/lib/error" import { state } from "~/lib/state" +/** + * Copilot does not support external image URLs — only base64 data URIs. + * This function downloads an external URL and converts it to a base64 data URI. + */ +async function fetchImageAsBase64(url: string): Promise { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Failed to fetch image from URL: ${url}`) + } + const contentType = response.headers.get("content-type") ?? "image/jpeg" + const buffer = await response.arrayBuffer() + const base64 = Buffer.from(buffer).toString("base64") + return `data:${contentType};base64,${base64}` +} + +/** + * Rewrite any external image_url entries in the payload to base64 data URIs + * so Copilot can process them. + */ +async function resolveExternalImages( + payload: ChatCompletionsPayload, +): Promise { + const hasExternalUrls = payload.messages.some( + (msg) => + Array.isArray(msg.content) + && msg.content.some( + (part) => + part.type === "image_url" && !part.image_url.url.startsWith("data:"), + ), + ) + + if (!hasExternalUrls) return payload + + const resolvedMessages = await Promise.all( + payload.messages.map(async (msg) => { + if (!Array.isArray(msg.content)) return msg + + const resolvedContent = await Promise.all( + msg.content.map(async (part) => { + if ( + part.type !== "image_url" + || part.image_url.url.startsWith("data:") + ) { + return part + } + try { + const base64Url = await fetchImageAsBase64(part.image_url.url) + return { + ...part, + image_url: { ...part.image_url, url: base64Url }, + } + } catch (err) { + consola.warn( + `Failed to fetch external image, skipping: ${part.image_url.url}`, + err, + ) + return part + } + }), + ) + + return { ...msg, content: resolvedContent } + }), + ) + + return { ...payload, messages: resolvedMessages } +} + export const createChatCompletions = async ( payload: ChatCompletionsPayload, ) => { if (!state.copilotToken) throw new Error("Copilot token not found") - const enableVision = payload.messages.some( + // Resolve external image URLs to base64 before sending to Copilot + const resolvedPayload = await resolveExternalImages(payload) + + const enableVision = resolvedPayload.messages.some( (x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"), @@ -18,7 +89,7 @@ export const createChatCompletions = async ( // Agent/user check for X-Initiator header // Determine if any message is from an agent ("assistant" or "tool") - const isAgentCall = payload.messages.some((msg) => + const isAgentCall = resolvedPayload.messages.some((msg) => ["assistant", "tool"].includes(msg.role), ) @@ -31,15 +102,19 @@ export const createChatCompletions = async ( const response = await fetch(`${copilotBaseUrl(state)}/chat/completions`, { method: "POST", headers, - body: JSON.stringify(payload), + body: JSON.stringify(resolvedPayload), }) if (!response.ok) { - consola.error("Failed to create chat completions", response) - throw new HTTPError("Failed to create chat completions", response) + const body = await response.text() + consola.error( + `Failed to create chat completions [model=${resolvedPayload.model}]`, + body, + ) + throw new HTTPError("Failed to create chat completions", response, body) } - if (payload.stream) { + if (resolvedPayload.stream) { return events(response) } diff --git a/src/services/copilot/get-models.ts b/src/services/copilot/get-models.ts index 3cfa30af0..15223fac4 100644 --- a/src/services/copilot/get-models.ts +++ b/src/services/copilot/get-models.ts @@ -7,7 +7,10 @@ export const getModels = async () => { headers: copilotHeaders(state), }) - if (!response.ok) throw new HTTPError("Failed to get models", response) + if (!response.ok) { + const body = await response.text() + throw new HTTPError("Failed to get models", response, body) + } return (await response.json()) as ModelsResponse } diff --git a/src/services/get-vscode-version.ts b/src/services/get-vscode-version.ts index 6078f09b5..fd2cd81fb 100644 --- a/src/services/get-vscode-version.ts +++ b/src/services/get-vscode-version.ts @@ -1,4 +1,4 @@ -const FALLBACK = "1.104.3" +const FALLBACK = "1.109.5" export async function getVSCodeVersion() { const controller = new AbortController() From b925f7834975e3a856e27420ca1542013fe0e9f2 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 19 Mar 2026 10:52:23 +0800 Subject: [PATCH 09/13] fix gpt-5.4 bug --- README.md | 36 +- package-lock.json | 7848 +++++++++++++++++ src/routes/chat-completions/handler.ts | 20 +- src/routes/messages/handler.ts | 19 +- src/routes/messages/non-stream-translation.ts | 12 +- src/services/copilot/create-responses.ts | 262 + src/services/copilot/get-models.ts | 1 + 7 files changed, 8191 insertions(+), 7 deletions(-) create mode 100644 package-lock.json create mode 100644 src/services/copilot/create-responses.ts diff --git a/README.md b/README.md index 0d36c13c9..7388df2c6 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,46 @@ https://github.com/user-attachments/assets/7654b383-669d-4eb9-b23c-06d7aefee8c5 ## Installation -To install dependencies, run: +### Global Installation (Recommended) + +Install `copilot-api` globally so you can use it as a command anywhere: + +```sh +# Using npm +npm install -g copilot-api + +# Using bun +bun install -g copilot-api +``` + +Once installed, run it directly: + +```sh +copilot-api start +``` + +### Using npx (No Install Required) + +Run without installing using npx — always fetches the latest version: + +```sh +npx copilot-api@latest start +``` + +### Installing from Source + +Clone the repository and install dependencies: ```sh bun install ``` +Then link it globally so the `copilot-api` command is available system-wide: + +```sh +npm link +``` + ## Using with Docker Build image diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..92d59f6b4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7848 @@ +{ + "name": "copilot-api", + "version": "0.7.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "copilot-api", + "version": "0.7.0", + "dependencies": { + "citty": "^0.1.6", + "clipboardy": "^5.0.0", + "consola": "^3.4.2", + "fetch-event-stream": "^0.1.5", + "gpt-tokenizer": "^3.0.1", + "hono": "^4.9.9", + "proxy-from-env": "^1.1.0", + "srvx": "^0.8.9", + "tiny-invariant": "^1.3.3", + "undici": "^7.16.0", + "zod": "^4.1.11" + }, + "bin": { + "copilot-api": "dist/main.js" + }, + "devDependencies": { + "@echristian/eslint-config": "^0.0.54", + "@types/bun": "^1.2.23", + "@types/proxy-from-env": "^1.0.4", + "bumpp": "^10.2.3", + "eslint": "^9.37.0", + "knip": "^5.64.1", + "lint-staged": "^16.2.3", + "prettier-plugin-packagejson": "^2.5.19", + "simple-git-hooks": "^2.13.1", + "tsdown": "^0.15.6", + "typescript": "^5.9.3" + } + }, + "node_modules/@altano/repository-tools": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@altano/repository-tools/-/repository-tools-2.0.1.tgz", + "integrity": "sha512-YE/52CkFtb+YtHPgbWPai7oo5N9AKnMuP5LM+i2AG7G1H2jdYBCO1iDnkDE3dZ3C1MIgckaF+d5PNRulgt0bdw==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@echristian/eslint-config": { + "version": "0.0.54", + "resolved": "https://registry.npmmirror.com/@echristian/eslint-config/-/eslint-config-0.0.54.tgz", + "integrity": "sha512-aR8vS932kZ9kW5ue1AhaYsTs0lwP0eITNzgBMAMnhBNq+8Sy2mP7I6m3zEzY3Mob4RsrBM5uY9H5SlnEw8+cEg==", + "dev": true, + "dependencies": { + "@eslint-react/eslint-plugin": "^1.52.7", + "@eslint/js": "^9.34.0", + "@eslint/json": "^0.13.2", + "@stylistic/eslint-plugin": "^5.2.3", + "defu": "^6.1.4", + "eslint-config-flat-gitignore": "^2.1.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-de-morgan": "^1.3.1", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-package-json": "^0.56.0", + "eslint-plugin-perfectionist": "^4.15.0", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-regexp": "^2.10.0", + "eslint-plugin-unicorn": "^60.0.0", + "eslint-plugin-unused-imports": "^4.2.0", + "globals": "^16.3.0", + "prettier": "^3.6.2", + "typescript-eslint": "^8.41.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint-react/ast": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/ast/-/ast-1.53.1.tgz", + "integrity": "sha512-qvUC99ewtriJp9quVEOvZ6+RHcsMLfVQ0OhZ4/LupZUDhjW7GiX1dxJsFaxHdJ9rLNLhQyLSPmbAToeqUrSruQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/typescript-estree": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/core": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/core/-/core-1.53.1.tgz", + "integrity": "sha512-8prroos5/Uvvh8Tjl1HHCpq4HWD3hV9tYkm7uXgKA6kqj0jHlgRcQzuO6ZPP7feBcK3uOeug7xrq03BuG8QKCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "birecord": "^0.1.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/eff": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/eff/-/eff-1.53.1.tgz", + "integrity": "sha512-uq20lPRAmsWRjIZm+mAV/2kZsU2nDqn5IJslxGWe3Vfdw23hoyhEw3S1KKlxbftwbTvsZjKvVP0iw3bZo/NUpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/eslint-plugin": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/eslint-plugin/-/eslint-plugin-1.53.1.tgz", + "integrity": "sha512-JZ2ciXNCC9CtBBAqYtwWH+Jy/7ZzLw+whei8atP4Fxsbh+Scs30MfEwBzuiEbNw6uF9eZFfPidchpr5RaEhqxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "eslint-plugin-react-debug": "1.53.1", + "eslint-plugin-react-dom": "1.53.1", + "eslint-plugin-react-hooks-extra": "1.53.1", + "eslint-plugin-react-naming-convention": "1.53.1", + "eslint-plugin-react-web-api": "1.53.1", + "eslint-plugin-react-x": "1.53.1" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@eslint-react/kit": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/kit/-/kit-1.53.1.tgz", + "integrity": "sha512-zOi2le9V4rMrJvQV4OeedGvMGvDT46OyFPOwXKs7m0tQu5vXVJ8qwIPaVQT1n/WIuvOg49OfmAVaHpGxK++xLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@typescript-eslint/utils": "^8.43.0", + "ts-pattern": "^5.8.0", + "zod": "^4.1.5" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/shared": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/shared/-/shared-1.53.1.tgz", + "integrity": "sha512-gomJQmFqQgQVI3Ra4vTMG/s6a4bx3JqeNiTBjxBJt4C9iGaBj458GkP4LJHX7TM6xUzX+fMSKOPX7eV3C/+UCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@typescript-eslint/utils": "^8.43.0", + "ts-pattern": "^5.8.0", + "zod": "^4.1.5" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/var": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/@eslint-react/var/-/var-1.53.1.tgz", + "integrity": "sha512-yzwopvPntcHU7mmDvWzRo1fb8QhjD8eDRRohD11rTV1u7nWO4QbJi0pOyugQakvte1/W11Y0Vr8Of0Ojk/A6zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint/compat": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/@eslint/compat/-/compat-2.0.2.tgz", + "integrity": "sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^8.40 || 9 || 10" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/compat/node_modules/@eslint/core": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.1.0.tgz", + "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.4", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", + "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.3", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.3", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.3.tgz", + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/json": { + "version": "0.13.2", + "resolved": "https://registry.npmmirror.com/@eslint/json/-/json-0.13.2.tgz", + "integrity": "sha512-yWLyRE18rHgHXhWigRpiyv1LDPkvWtC6oa7QHXW7YdP6gosJoq7BiLZW2yCs9U7zN7X4U3ZeOJjepA10XAOIMw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "@eslint/plugin-kit": "^0.3.5", + "@humanwhocodes/momoa": "^3.3.9", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/momoa": { + "version": "3.3.10", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/momoa/-/momoa-3.3.10.tgz", + "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.95.0", + "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.95.0.tgz", + "integrity": "sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxc-resolver/binding-android-arm-eabi": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.19.0.tgz", + "integrity": "sha512-dlMjjWE3h+qMujLp5nBX/x7R5ny+xfr4YtsyaMNuM5JImOtQBzpFxQr9kJOKGL+9RbaoTOXpt5KF05f9pnOsgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-android-arm64": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.19.0.tgz", + "integrity": "sha512-x5P0Y12oMcSC9PKkz1FtdVVLosXYi/05m+ufxPrUggd6vZRBPJhW4zZUsMVbz8dwwk71Dh0f6/2ntw3WPOq+Ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-arm64": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.19.0.tgz", + "integrity": "sha512-DjnuIPB60IQrVSCiuVBzN8/8AeeIjthdkk+dZYdZzgLeP2T5ZF41u50haJMtIdGr5cRzRH6zPV/gh6+RFjlvKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.19.0.tgz", + "integrity": "sha512-dVAqIZIIY7xOXCCV0nJPs8ExlYc6R7mcNpFobwNyE3qlXGbgvwb7Gl3iOumOiPBfF+sbJR3MMP7RAPfKqbvYyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-freebsd-x64": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.19.0.tgz", + "integrity": "sha512-kwcZ30bIpJNFcT22sIlde4mz0EyXmB3lAefCFWtffqpbmLweQUwz1dKDcsutxEjpkbEKLmfrj1wCyRZp7n5Hnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.19.0.tgz", + "integrity": "sha512-GImk/cb3X+zBGEwr6l9h0dbiNo5zNd52gamZmluEpbyybiZ8kc5q44/7zRR4ILChWRW7pI92W57CJwhkF+wRmg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.19.0.tgz", + "integrity": "sha512-uIEyws3bBD1gif4SZCOV2XIr6q5fd1WbzzBbpL8qk+TbzOvKMWnMNNtfNacnAGGa2lLRNXR1Fffot2mlZ/Xmbw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.19.0.tgz", + "integrity": "sha512-bIkgp+AB+yZfvdKDfjFT7PycsRtih7+zCV5AbnkzfyvNvQ47rfssf8R1IbG++mx+rZ4YUCUu8EbP66HC3O5c5w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-musl": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.19.0.tgz", + "integrity": "sha512-bOt5pKPcbidTSy64m2CfM0XcaCmxBEFclCMPuOPO08hh8QIFTiZVhFf/OxTFqyRwhq/tlzzKmXpMo7DfzbO5lQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.19.0.tgz", + "integrity": "sha512-BymEPqVeLZzA/1kXow9U9rdniq1r5kk4u686Cx3ZU77YygR48NJI/2TyjM70vKHZffGx75ZShobcc1M5GXG3WA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.19.0.tgz", + "integrity": "sha512-aFgPTzZZY+XCYe4B+3A1S63xcIh2i136+2TPXWr9NOwXXTdMdBntb1J9fEgxXDnX82MjBknLUpJqAZHNTJzixA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.19.0.tgz", + "integrity": "sha512-9WDGt7fV9GK97WrWE/VEDhMFv9m0ZXYn5NQ+16QvyT0ux8yGLAvyadi6viaTjEdJII/OaHBRYHcL+zUjmaWwmg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.19.0.tgz", + "integrity": "sha512-SY3di6tccocppAVal5Hev3D6D1N5Y6TCEypAvNCOiPqku2Y8U/aXfvGbthqdPNa72KYqjUR1vomOv6J9thHITA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.19.0.tgz", + "integrity": "sha512-SV+4zBeCC3xjSE2wvhN45eyABoVRX3xryWBABFKfLwAWhF3wsB3bUF+CantYfQ/TLpasyvplRS9ovvFT9cb/0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-musl": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.19.0.tgz", + "integrity": "sha512-LkbjO+r5Isl8Xl29pJYOCB/iSUIULFUJDGdMp+yJD3OgWtSa6VJta2iw7QXmpcoOkq18UIL09yWrlyjLDL0Hug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-openharmony-arm64": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.19.0.tgz", + "integrity": "sha512-Ud1gelL5slpEU5AjzBWQz1WheprOAl5CPnCKTWynvvdlBbAZXA6fPYLuCrlRo0uw+x3f37XJ71kirpSew8Zyvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.19.0.tgz", + "integrity": "sha512-wXLNAVmL4vWXKaYJnFPgg5zQsSr3Rv+ftNReIU3UkzTcoVLK0805Pnbr2NwcBWSO5hhpOEdys02qlT2kxVgjWw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.19.0.tgz", + "integrity": "sha512-zszvr0dJfvv0Jg49hLwjAJ4SRzfsq28SoearUtT1qv3qXRYsBWuctdlRa/lEZkiuG4tZWiY425Jh9QqLafwsAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-ia32-msvc": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.19.0.tgz", + "integrity": "sha512-I7ZYujr5XL1l7OwuddbOeqdUyFOaf51W1U2xUogInFdupIAKGqbpugpAK6RaccLcSlN0bbuo3CS5h7ue38SUAg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-x64-msvc": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.19.0.tgz", + "integrity": "sha512-NxErbI1TmJEZZVvGPePjgXFZCuOzrjQuJ6YwHjcWkelReK7Uhg4QeL05zRdfTpgkH6IY/C8OjbKx5ZilQ4yDFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@quansync/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@quansync/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.45.tgz", + "integrity": "sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.45.tgz", + "integrity": "sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.45.tgz", + "integrity": "sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.45.tgz", + "integrity": "sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.45.tgz", + "integrity": "sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.45.tgz", + "integrity": "sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.45.tgz", + "integrity": "sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.45.tgz", + "integrity": "sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.45.tgz", + "integrity": "sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.45.tgz", + "integrity": "sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.45.tgz", + "integrity": "sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.45.tgz", + "integrity": "sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-ia32-msvc": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.45.tgz", + "integrity": "sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.45.tgz", + "integrity": "sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.45.tgz", + "integrity": "sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.9.0", + "resolved": "https://registry.npmmirror.com/@stylistic/eslint-plugin/-/eslint-plugin-5.9.0.tgz", + "integrity": "sha512-FqqSkvDMYJReydrMhlugc71M76yLLQWNfmGq+SIlLa7N3kHp8Qq8i2PyWrVNAfjOyOIY+xv9XaaYwvVW7vroMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.0.0 || ^10.0.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/bun": { + "version": "1.3.9", + "resolved": "https://registry.npmmirror.com/@types/bun/-/bun-1.3.9.tgz", + "integrity": "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.3.9" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.3.2", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.3.2.tgz", + "integrity": "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/proxy-from-env": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/@types/proxy-from-env/-/proxy-from-env-1.0.4.tgz", + "integrity": "sha512-TPR9/bCZAr3V1eHN4G3LD3OLicdJjqX1QRXWuNcCYgE66f/K8jO2ZRtHxI2D9MbnuUP6+qiKSS8eUHp6TFHGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/args-tokenizer": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/args-tokenizer/-/args-tokenizer-0.3.0.tgz", + "integrity": "sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.1", + "resolved": "https://registry.npmmirror.com/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/birecord": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/birecord/-/birecord-0.1.1.tgz", + "integrity": "sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==", + "dev": true, + "license": "(MIT OR Apache-2.0)" + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/builtin-modules": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-5.0.0.tgz", + "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bumpp": { + "version": "10.4.1", + "resolved": "https://registry.npmmirror.com/bumpp/-/bumpp-10.4.1.tgz", + "integrity": "sha512-X/bwWs5Gbb/D7rN4aHLB7zdjiA6nGdjckM1sTHhI9oovIbEw2L5pw5S4xzk8ZTeOZ8EnwU/Ze4SoZ6/Vr3pM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.2.0", + "args-tokenizer": "^0.3.0", + "c12": "^3.3.3", + "cac": "^6.7.14", + "escalade": "^3.2.0", + "jsonc-parser": "^3.3.1", + "package-manager-detector": "^1.6.0", + "semver": "^7.7.3", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "yaml": "^2.8.2" + }, + "bin": { + "bumpp": "bin/bumpp.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/bun-types": { + "version": "1.3.9", + "resolved": "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.9.tgz", + "integrity": "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/c12": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/c12/-/c12-3.3.3.tgz", + "integrity": "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^17.2.3", + "exsolve": "^1.0.8", + "giget": "^2.0.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "*" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001774", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmmirror.com/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboard-image": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/clipboard-image/-/clipboard-image-0.1.0.tgz", + "integrity": "sha512-SWk7FgaXLNFld19peQ/rTe0n97lwR1WbkqxV6JKCAOh7U52AKV/PeMFCyt/8IhBdqyDA8rdyewQMKZqvWT5Akg==", + "license": "MIT", + "dependencies": { + "run-jxa": "^3.0.0" + }, + "bin": { + "clipboard-image": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-5.3.1.tgz", + "integrity": "sha512-fPWgBqpp9ctiOQCkE5yjYGzv11ZU55g6ahEgr3COiio6dXdt1mbchCPXQrSR2Y9sZqfi8L7QD3+UosgXVIuPdg==", + "license": "MIT", + "dependencies": { + "clipboard-image": "^0.1.0", + "execa": "^9.6.1", + "is-wayland": "^0.1.0", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0", + "powershell-utils": "^0.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/comment-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/comment-parser/-/comment-parser-1.4.5.tgz", + "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmmirror.com/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.48.0", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.48.0.tgz", + "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-newline": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/detect-newline/-/detect-newline-4.0.1.tgz", + "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmmirror.com/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dts-resolver": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/dts-resolver/-/dts-resolver-2.1.3.tgz", + "integrity": "sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "oxc-resolver": ">=11.0.0" + }, + "peerDependenciesMeta": { + "oxc-resolver": { + "optional": true + } + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.302", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.3", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.3.tgz", + "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.3", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-flat-gitignore": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/eslint-config-flat-gitignore/-/eslint-config-flat-gitignore-2.2.1.tgz", + "integrity": "sha512-wA5EqN0era7/7Gt5Botlsfin/UNY0etJSEeBgbUlFLFrBi47rAN//+39fI7fpYcl8RENutlFtvp/zRa/M/pZNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint/compat": "^2.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "eslint": "^9.5.0 || ^10.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-fix-utils": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/eslint-fix-utils/-/eslint-fix-utils-0.4.1.tgz", + "integrity": "sha512-1xPtnB7RYRHKrFGll3kRv5gOodHm3/jkk76jrKMZ2yk/G8HU9XoN1I9iHgh1ToAqmGG0/FFrybZMqmqUWp4asA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/estree": ">=1", + "eslint": ">=8" + }, + "peerDependenciesMeta": { + "@types/estree": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-de-morgan": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-de-morgan/-/eslint-plugin-de-morgan-1.3.1.tgz", + "integrity": "sha512-pB0xqHPXCRgCFnFSLvQDSP/egYrlccYMI0txz4gzBF6RuT2X+4LsZl0JoWAQc7dphEjJBQ3dWyMqXfBgdP2UVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "eslint": ">=8.0.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmmirror.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-package-json": { + "version": "0.56.4", + "resolved": "https://registry.npmmirror.com/eslint-plugin-package-json/-/eslint-plugin-package-json-0.56.4.tgz", + "integrity": "sha512-C2/jmPf3uUBEYA9ZYBLxu+9hv8rUWBKMtgrqoiCPfg/RZ8YzkNTgEA0dMeEswQVNdqt4fgbz7uOfYXYoiIKm4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@altano/repository-tools": "^2.0.1", + "change-case": "^5.4.4", + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.1", + "eslint-fix-utils": "~0.4.0", + "package-json-validator": "~0.30.0", + "semver": "^7.5.4", + "sort-object-keys": "^1.1.3", + "sort-package-json": "^3.3.0", + "validate-npm-package-name": "^6.0.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "eslint": ">=8.0.0", + "jsonc-eslint-parser": "^2.0.0" + } + }, + "node_modules/eslint-plugin-perfectionist": { + "version": "4.15.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-4.15.1.tgz", + "integrity": "sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/utils": "^8.38.0", + "natural-orderby": "^5.0.0" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "eslint": ">=8.45.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-debug": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-debug/-/eslint-plugin-react-debug-1.53.1.tgz", + "integrity": "sha512-WNOiQ6jhodJE88VjBU/IVDM+2Zr9gKHlBFDUSA3fQ0dMB5RiBVj5wMtxbxRuipK/GqNJbteqHcZoYEod7nfddg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-dom": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-dom/-/eslint-plugin-react-dom-1.53.1.tgz", + "integrity": "sha512-UYrWJ2cS4HpJ1A5XBuf1HfMpPoLdfGil+27g/ldXfGemb4IXqlxHt4ANLyC8l2CWcE3SXGJW7mTslL34MG0qTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "compare-versions": "^6.1.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-hooks-extra": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks-extra/-/eslint-plugin-react-hooks-extra-1.53.1.tgz", + "integrity": "sha512-fshTnMWNn9NjFLIuy7HzkRgGK29vKv4ZBO9UMr+kltVAfKLMeXXP6021qVKk66i/XhQjbktiS+vQsu1Rd3ZKvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-naming-convention": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-naming-convention/-/eslint-plugin-react-naming-convention-1.53.1.tgz", + "integrity": "sha512-rvZ/B/CSVF8d34HQ4qIt90LRuxotVx+KUf3i1OMXAyhsagEFMRe4gAlPJiRufZ+h9lnuu279bEdd+NINsXOteA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-web-api": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-web-api/-/eslint-plugin-react-web-api-1.53.1.tgz", + "integrity": "sha512-INVZ3Cbl9/b+sizyb43ChzEPXXYuDsBGU9BIg7OVTNPyDPloCXdI+dQFAcSlDocZhPrLxhPV3eT6+gXbygzYXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-x": { + "version": "1.53.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-x/-/eslint-plugin-react-x-1.53.1.tgz", + "integrity": "sha512-MwMNnVwiPem0U6SlejDF/ddA4h/lmP6imL1RDZ2m3pUBrcdcOwOx0gyiRVTA3ENnhRlWfHljHf5y7m8qDSxMEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "compare-versions": "^6.1.1", + "is-immutable-type": "^5.0.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "ts-api-utils": "^2.1.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "ts-api-utils": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-regexp": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-regexp/-/eslint-plugin-regexp-2.10.0.tgz", + "integrity": "sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "comment-parser": "^1.4.0", + "jsdoc-type-pratt-parser": "^4.0.0", + "refa": "^0.12.1", + "regexp-ast-analysis": "^0.7.1", + "scslre": "^0.3.0" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "eslint": ">=8.44.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "60.0.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.44.0", + "esquery": "^1.6.0", + "find-up-simple": "^1.0.1", + "globals": "^16.3.0", + "indent-string": "^5.0.0", + "is-builtin-module": "^5.0.0", + "jsesc": "^3.1.0", + "pluralize": "^8.0.0", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.12.0", + "semver": "^7.7.2", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": "^20.10.0 || >=21.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=9.29.0" + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", + "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/fd-package-json/-/fd-package-json-2.0.0.tgz", + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-up-path": "^4.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-event-stream": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/fetch-event-stream/-/fetch-event-stream-0.1.6.tgz", + "integrity": "sha512-GREtJ5HNikdU2AXtZ6E/5bk+aslMU6ie5mPG6H9nvsdDkkHQ6m5lHwmmmDTOBexok9hApQ7EprsXCdmz9ZC68w==", + "license": "MIT" + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/formatly": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/formatly/-/formatly-0.3.0.tgz", + "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fd-package-json": "^2.0.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/git-hooks-list": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/git-hooks-list/-/git-hooks-list-4.2.1.tgz", + "integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gpt-tokenizer": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/gpt-tokenizer/-/gpt-tokenizer-3.4.0.tgz", + "integrity": "sha512-wxFLnhIXTDjYebd9A9pGl3e31ZpSypbpIJSOswbgop5jLte/AsZVDvjlbEuVFlsqZixVKqbcoNmRlFDf6pz/UQ==", + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/hono/-/hono-4.12.3.tgz", + "integrity": "sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-5.0.0.tgz", + "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^5.0.0" + }, + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-immutable-type": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/is-immutable-type/-/is-immutable-type-5.0.1.tgz", + "integrity": "sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@typescript-eslint/type-utils": "^8.0.0", + "ts-api-utils": "^2.0.0", + "ts-declaration-location": "^1.0.4" + }, + "peerDependencies": { + "eslint": "*", + "typescript": ">=4.7.4" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wayland": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/is-wayland/-/is-wayland-0.1.0.tgz", + "integrity": "sha512-QkbMsWkIfkrzOPxenwye0h56iAXirZYHG9eHVPb22fO9y+wPbaX/CHacOWBa/I++4ohTcByimhM1/nyCsH8KNA==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is64bit": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is64bit/-/is64bit-2.0.0.tgz", + "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.8.0", + "resolved": "https://registry.npmmirror.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.8.0.tgz", + "integrity": "sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonc-eslint-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz", + "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/jsonc-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/jsonc-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmmirror.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/knip": { + "version": "5.85.0", + "resolved": "https://registry.npmmirror.com/knip/-/knip-5.85.0.tgz", + "integrity": "sha512-V2kyON+DZiYdNNdY6GALseiNCwX7dYdpz9Pv85AUn69Gk0UKCts+glOKWfe5KmaMByRjM9q17Mzj/KinTVOyxg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + } + ], + "license": "ISC", + "dependencies": { + "@nodelib/fs.walk": "^1.2.3", + "fast-glob": "^3.3.3", + "formatly": "^0.3.0", + "jiti": "^2.6.0", + "js-yaml": "^4.1.1", + "minimist": "^1.2.8", + "oxc-resolver": "^11.15.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.1", + "smol-toml": "^1.5.2", + "strip-json-comments": "5.0.3", + "zod": "^4.1.11" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4 <7" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmmirror.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmmirror.com/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/macos-version": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/macos-version/-/macos-version-6.0.0.tgz", + "integrity": "sha512-O2S8voA+pMfCHhBn/TIYDXzJ1qNHpPDU32oFxglKnVdJABiYYITt45oLkV9yhwA3E2FDwn3tQqUFrTsr1p3sBQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-orderby": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oxc-resolver": { + "version": "11.19.0", + "resolved": "https://registry.npmmirror.com/oxc-resolver/-/oxc-resolver-11.19.0.tgz", + "integrity": "sha512-oEe42WEoZc2T5sCQqgaRBx8huzP4cJvrnm+BfNTJESdtM633Tqs6iowkpsMTXgnb7SLwU6N6D9bqwW/PULjo6A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.19.0", + "@oxc-resolver/binding-android-arm64": "11.19.0", + "@oxc-resolver/binding-darwin-arm64": "11.19.0", + "@oxc-resolver/binding-darwin-x64": "11.19.0", + "@oxc-resolver/binding-freebsd-x64": "11.19.0", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.19.0", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.19.0", + "@oxc-resolver/binding-linux-arm64-gnu": "11.19.0", + "@oxc-resolver/binding-linux-arm64-musl": "11.19.0", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.19.0", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.19.0", + "@oxc-resolver/binding-linux-riscv64-musl": "11.19.0", + "@oxc-resolver/binding-linux-s390x-gnu": "11.19.0", + "@oxc-resolver/binding-linux-x64-gnu": "11.19.0", + "@oxc-resolver/binding-linux-x64-musl": "11.19.0", + "@oxc-resolver/binding-openharmony-arm64": "11.19.0", + "@oxc-resolver/binding-wasm32-wasi": "11.19.0", + "@oxc-resolver/binding-win32-arm64-msvc": "11.19.0", + "@oxc-resolver/binding-win32-ia32-msvc": "11.19.0", + "@oxc-resolver/binding-win32-x64-msvc": "11.19.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-validator": { + "version": "0.30.1", + "resolved": "https://registry.npmmirror.com/package-json-validator/-/package-json-validator-0.30.1.tgz", + "integrity": "sha512-OCwQgxkbKYfeI3fJPsbp+IeXSZnpWN9CWPTFOiwfIPGMdH6eT+uI45sM7vkXeoBlCdJ8hpwEV7oHVHyALJiaIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.2", + "validate-npm-package-license": "^3.0.4", + "yargs": "~18.0.0" + }, + "bin": { + "pjv": "lib/bin/pjv.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/powershell-utils": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/powershell-utils/-/powershell-utils-0.2.0.tgz", + "integrity": "sha512-ZlsFlG7MtSFCoc5xreOvBAozCJ6Pf06opgJjh9ONEv418xpZSAzNjstD36C6+JwOnfSqOW/9uDkqKjezTdxZhw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-packagejson": { + "version": "2.5.22", + "resolved": "https://registry.npmmirror.com/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.22.tgz", + "integrity": "sha512-G6WalmoUssKF8ZXkni0+n4324K+gG143KPysSQNW+FrR0XyNb3BdRxchGC/Q1FE/F702p7/6KU7r4mv0WSWbzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sort-package-json": "3.6.0" + }, + "peerDependencies": { + "prettier": ">= 1.16.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/prettier-plugin-packagejson/node_modules/sort-object-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/sort-object-keys/-/sort-object-keys-2.1.0.tgz", + "integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/prettier-plugin-packagejson/node_modules/sort-package-json": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/sort-package-json/-/sort-package-json-3.6.0.tgz", + "integrity": "sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^7.0.2", + "detect-newline": "^4.0.1", + "git-hooks-list": "^4.1.1", + "is-plain-obj": "^4.1.0", + "semver": "^7.7.3", + "sort-object-keys": "^2.0.1", + "tinyglobby": "^0.2.15" + }, + "bin": { + "sort-package-json": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/refa": { + "version": "0.12.1", + "resolved": "https://registry.npmmirror.com/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmmirror.com/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmmirror.com/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.0.0-beta.45", + "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.0.0-beta.45.tgz", + "integrity": "sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.95.0", + "@rolldown/pluginutils": "1.0.0-beta.45" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-beta.45", + "@rolldown/binding-darwin-arm64": "1.0.0-beta.45", + "@rolldown/binding-darwin-x64": "1.0.0-beta.45", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.45", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.45", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.45", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.45", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.45", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.45", + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.45", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.45", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.45", + "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.45", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.45" + } + }, + "node_modules/rolldown-plugin-dts": { + "version": "0.17.8", + "resolved": "https://registry.npmmirror.com/rolldown-plugin-dts/-/rolldown-plugin-dts-0.17.8.tgz", + "integrity": "sha512-76EEBlhF00yeY6M7VpMkWKI4r9WjuoMiOGey7j4D6zf3m0BR+ZrrY9hvSXdueJ3ljxSLq4DJBKFpX/X9+L7EKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.28.5", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "ast-kit": "^2.2.0", + "birpc": "^2.8.0", + "dts-resolver": "^2.1.3", + "get-tsconfig": "^4.13.0", + "magic-string": "^0.30.21", + "obug": "^2.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@ts-macro/tsc": "^0.3.6", + "@typescript/native-preview": ">=7.0.0-dev.20250601.1", + "rolldown": "^1.0.0-beta.44", + "typescript": "^5.0.0", + "vue-tsc": "~3.1.0" + }, + "peerDependenciesMeta": { + "@ts-macro/tsc": { + "optional": true + }, + "@typescript/native-preview": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/run-jxa": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/run-jxa/-/run-jxa-3.0.0.tgz", + "integrity": "sha512-4f2CrY7H+sXkKXJn/cE6qRA3z+NMVO7zvlZ/nUV0e62yWftpiLAfw5eV9ZdomzWd2TXWwEIiGjAT57+lWIzzvA==", + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "macos-version": "^6.0.0", + "subsume": "^4.0.0", + "type-fest": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-jxa/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-jxa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-jxa/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-jxa/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-jxa/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-jxa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-jxa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/run-jxa/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-git-hooks": { + "version": "2.13.1", + "resolved": "https://registry.npmmirror.com/simple-git-hooks/-/simple-git-hooks-2.13.1.tgz", + "integrity": "sha512-WszCLXwT4h2k1ufIXAgsbiTOazqqevFCIncOuUBZJ91DdvWcC5+OFkluWRQPrcuSYd8fjq+o2y1QfWqYMoAToQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "simple-git-hooks": "cli.js" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smol-toml": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sort-package-json": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/sort-package-json/-/sort-package-json-3.6.1.tgz", + "integrity": "sha512-Chgejw1+10p2D0U2tB7au1lHtz6TkFnxmvZktyBCRyV0GgmF6nl1IxXxAsPtJVsUyg/fo+BfCMAVVFUVRkAHrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^7.0.2", + "detect-newline": "^4.0.1", + "git-hooks-list": "^4.1.1", + "is-plain-obj": "^4.1.0", + "semver": "^7.7.3", + "sort-object-keys": "^2.0.1", + "tinyglobby": "^0.2.15" + }, + "bin": { + "sort-package-json": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sort-package-json/node_modules/sort-object-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/sort-object-keys/-/sort-object-keys-2.1.0.tgz", + "integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/srvx": { + "version": "0.8.16", + "resolved": "https://registry.npmmirror.com/srvx/-/srvx-0.8.16.tgz", + "integrity": "sha512-hmcGW4CgroeSmzgF1Ihwgl+Ths0JqAJ7HwjP2X7e3JzY7u4IydLMcdnlqGQiQGUswz+PO9oh/KtCpOISIvs9QQ==", + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-ts": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/string-ts/-/string-ts-2.3.1.tgz", + "integrity": "sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "8.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/subsume": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/subsume/-/subsume-4.0.0.tgz", + "integrity": "sha512-BWnYJElmHbYZ/zKevy+TG+SsyoFCmRPDHJbR1MzLxkPOv1Jp/4hGhVUtP98s+wZBsBsHwCXvPTP0x287/WMjGg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0", + "unique-string": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/subsume/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/system-architecture": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/system-architecture/-/system-architecture-0.1.0.tgz", + "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-declaration-location": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", + "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", + "dev": true, + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/ts-pattern": { + "version": "5.9.0", + "resolved": "https://registry.npmmirror.com/ts-pattern/-/ts-pattern-5.9.0.tgz", + "integrity": "sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tsdown": { + "version": "0.15.12", + "resolved": "https://registry.npmmirror.com/tsdown/-/tsdown-0.15.12.tgz", + "integrity": "sha512-c8VLlQm8/lFrOAg5VMVeN4NAbejZyVQkzd+ErjuaQgJFI/9MhR9ivr0H/CM7UlOF1+ELlF6YaI7sU/4itgGQ8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.2.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "debug": "^4.4.3", + "diff": "^8.0.2", + "empathic": "^2.0.0", + "hookable": "^5.5.3", + "rolldown": "1.0.0-beta.45", + "rolldown-plugin-dts": "^0.17.2", + "semver": "^7.7.3", + "tinyexec": "^1.0.1", + "tinyglobby": "^0.2.15", + "tree-kill": "^1.2.2", + "unconfig": "^7.3.3" + }, + "bin": { + "tsdown": "dist/run.mjs" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@arethetypeswrong/core": "^0.18.1", + "publint": "^0.3.0", + "typescript": "^5.0.0", + "unplugin-lightningcss": "^0.4.0", + "unplugin-unused": "^0.5.0", + "unrun": "^0.2.1" + }, + "peerDependenciesMeta": { + "@arethetypeswrong/core": { + "optional": true + }, + "publint": { + "optional": true + }, + "typescript": { + "optional": true + }, + "unplugin-lightningcss": { + "optional": true + }, + "unplugin-unused": { + "optional": true + }, + "unrun": { + "optional": true + } + } + }, + "node_modules/tsdown/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tsdown/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.56.1", + "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.56.1.tgz", + "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.56.1", + "@typescript-eslint/parser": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unconfig": { + "version": "7.5.0", + "resolved": "https://registry.npmmirror.com/unconfig/-/unconfig-7.5.0.tgz", + "integrity": "sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "defu": "^6.1.4", + "jiti": "^2.6.1", + "quansync": "^1.0.0", + "unconfig-core": "7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://registry.npmmirror.com/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmmirror.com/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmmirror.com/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/src/routes/chat-completions/handler.ts b/src/routes/chat-completions/handler.ts index 04a5ae9ed..abe39acb5 100644 --- a/src/routes/chat-completions/handler.ts +++ b/src/routes/chat-completions/handler.ts @@ -13,6 +13,7 @@ import { type ChatCompletionResponse, type ChatCompletionsPayload, } from "~/services/copilot/create-chat-completions" +import { createChatCompletionsViaResponses } from "~/services/copilot/create-responses" export async function handleCompletion(c: Context) { await checkRateLimit(state) @@ -47,7 +48,23 @@ export async function handleCompletion(c: Context) { consola.debug("Set max_tokens to:", JSON.stringify(payload.max_tokens)) } - const response = await createChatCompletions(payload) + // Some models (e.g. gpt-5.4, gpt-5.x-codex) only support /responses, not + // /chat/completions. Route them through the responses adapter instead. + const supportedEndpoints = selectedModel?.supported_endpoints ?? [] + const useResponsesEndpoint = + supportedEndpoints.length > 0 && + supportedEndpoints.includes("/responses") && + !supportedEndpoints.includes("/chat/completions") + + if (useResponsesEndpoint) { + consola.info( + `Model "${payload.model}" only supports /responses — routing via responses adapter`, + ) + } + + const response = useResponsesEndpoint + ? await createChatCompletionsViaResponses(payload) + : await createChatCompletions(payload) if (isNonStreaming(response)) { consola.debug("Non-streaming response:", JSON.stringify(response)) @@ -66,3 +83,4 @@ export async function handleCompletion(c: Context) { const isNonStreaming = ( response: Awaited>, ): response is ChatCompletionResponse => Object.hasOwn(response, "choices") + diff --git a/src/routes/messages/handler.ts b/src/routes/messages/handler.ts index 13e98677e..933c99814 100644 --- a/src/routes/messages/handler.ts +++ b/src/routes/messages/handler.ts @@ -12,6 +12,7 @@ import { type ChatCompletionChunk, type ChatCompletionResponse, } from "~/services/copilot/create-chat-completions" +import { createChatCompletionsViaResponses } from "~/services/copilot/create-responses" import { type AnthropicMessagesPayload, @@ -57,7 +58,23 @@ export async function handleCompletion(c: Context) { await awaitApproval() } - const response = await createChatCompletions(openAIPayload) + // Some models (e.g. gpt-5.4, gpt-5.x-codex) only support /responses, not + // /chat/completions. Route them through the responses adapter instead. + const supportedEndpoints = selectedModel?.supported_endpoints ?? [] + const useResponsesEndpoint = + supportedEndpoints.length > 0 && + supportedEndpoints.includes("/responses") && + !supportedEndpoints.includes("/chat/completions") + + if (useResponsesEndpoint) { + consola.info( + `Model "${openAIPayload.model}" only supports /responses — routing via responses adapter`, + ) + } + + const response = useResponsesEndpoint + ? await createChatCompletionsViaResponses(openAIPayload) + : await createChatCompletions(openAIPayload) if (isNonStreaming(response)) { consola.debug( diff --git a/src/routes/messages/non-stream-translation.ts b/src/routes/messages/non-stream-translation.ts index 3fd20f5f6..cc69fcfc9 100644 --- a/src/routes/messages/non-stream-translation.ts +++ b/src/routes/messages/non-stream-translation.ts @@ -68,15 +68,19 @@ function translateModelName(model: string): string { result = result.replace(bracketSuffixPattern, "") } - // Strip 8-digit date suffixes like -20250514 - const dateSuffixPattern = /-\d{8}$/ + // Strip date suffixes in either compact or dashed form: + // -20250514 (8 consecutive digits, e.g. from Anthropic API) + // -2025-04-14 (YYYY-MM-DD, e.g. "gpt-4.1-2025-04-14" from OpenAI) + const dateSuffixPattern = /(-\d{8}|-\d{4}-\d{2}-\d{2})$/ if (dateSuffixPattern.test(result)) { result = result.replace(dateSuffixPattern, "") } // Convert hyphen minor version to dot: "claude-haiku-4-5" → "claude-haiku-4.5" - // Matches: word-N-N at the end where the last segment is a short version number - const hyphenMinorVersionPattern = /^(.*-\d+)-(\d+)$/ + // Only applies when the last two segments are both short numbers (1-2 digits), + // to avoid mismatching version-like segments in model names that already had + // their date suffix stripped (e.g. "gpt-4.1" should stay as-is). + const hyphenMinorVersionPattern = /^(.*-\d+)-(\d{1,2})$/ const match = hyphenMinorVersionPattern.exec(result) if (match) { result = `${match[1]}.${match[2]}` diff --git a/src/services/copilot/create-responses.ts b/src/services/copilot/create-responses.ts new file mode 100644 index 000000000..5a99067fa --- /dev/null +++ b/src/services/copilot/create-responses.ts @@ -0,0 +1,262 @@ +import consola from "consola" +import { events } from "fetch-event-stream" + +import { copilotBaseUrl, copilotHeaders } from "~/lib/api-config" +import { HTTPError } from "~/lib/error" +import { state } from "~/lib/state" +import type { + ChatCompletionChunk, + ChatCompletionResponse, + ChatCompletionsPayload, +} from "./create-chat-completions" + +/** + * Some models (e.g. gpt-5.4, gpt-5.x-codex) only support the /responses + * endpoint rather than /chat/completions. This module calls /responses and + * translates the response back into the standard chat-completions shape so the + * rest of the proxy can treat all models uniformly. + */ + +// ---------- /responses request / response types ---------- + +interface ResponsesInput { + role: string + content: string | Array<{ type: string; text?: string; image_url?: { url: string; detail?: string } }> +} + +interface ResponsesPayload { + model: string + input: ResponsesInput[] + stream?: boolean | null + temperature?: number | null + top_p?: number | null + max_output_tokens?: number | null + tools?: unknown[] | null + tool_choice?: unknown +} + +interface ResponsesOutputContent { + type: string + text?: string + annotations?: unknown[] +} + +interface ResponsesOutputMessage { + type: "message" + role: "assistant" + content: ResponsesOutputContent[] + status: string +} + +interface ResponsesUsage { + input_tokens: number + output_tokens: number + total_tokens: number +} + +interface ResponsesResponse { + id: string + model: string + object: "response" + status: string + output: ResponsesOutputMessage[] + usage?: ResponsesUsage +} + +// ---------- streaming event types ---------- + +interface ResponsesStreamEvent { + type: string + // response.created + response?: ResponsesResponse + // response.output_item.added / response.output_text.delta + item?: ResponsesOutputMessage + output_index?: number + content_index?: number + delta?: string + // response.completed + usage?: ResponsesUsage +} + +// ---------- converters ---------- + +function toResponsesPayload(payload: ChatCompletionsPayload): ResponsesPayload { + const input: ResponsesInput[] = payload.messages.map((msg) => ({ + role: msg.role === "developer" ? "system" : msg.role, + content: + typeof msg.content === "string" || msg.content === null + ? (msg.content ?? "") + : msg.content.map((part) => { + if (part.type === "text") return { type: "text", text: part.text } + return { + type: "image_url", + image_url: (part as { type: "image_url"; image_url: { url: string; detail?: string } }).image_url, + } + }), + })) + + // /responses API uses a flat tool format: { type, name, description, parameters } + // whereas /chat/completions uses { type, function: { name, description, parameters } } + const tools = payload.tools?.map((tool) => ({ + type: tool.type, + name: tool.function.name, + description: tool.function.description, + parameters: tool.function.parameters, + })) ?? null + + return { + model: payload.model, + input, + stream: payload.stream, + temperature: payload.temperature, + top_p: payload.top_p, + max_output_tokens: payload.max_tokens, + tools, + tool_choice: payload.tool_choice, + } +} + +function responsesResponseToChatCompletion( + res: ResponsesResponse, + requestModel: string, +): ChatCompletionResponse { + const text = + res.output + .filter((item) => item.type === "message") + .flatMap((item) => item.content) + .filter((c) => c.type === "output_text") + .map((c) => c.text ?? "") + .join("") || null + + return { + id: res.id, + object: "chat.completion", + created: Math.floor(Date.now() / 1000), + model: requestModel, + choices: [ + { + index: 0, + message: { role: "assistant", content: text }, + logprobs: null, + finish_reason: res.status === "completed" ? "stop" : "length", + }, + ], + usage: res.usage + ? { + prompt_tokens: res.usage.input_tokens, + completion_tokens: res.usage.output_tokens, + total_tokens: res.usage.total_tokens, + } + : undefined, + } +} + +// ---------- streaming translation ---------- + +async function* translateResponsesStream( + source: AsyncIterable<{ data?: string; event?: string }>, + requestModel: string, + responseId: string, +): AsyncGenerator<{ data: string; event?: string }> { + const created = Math.floor(Date.now() / 1000) + const id = responseId + + for await (const raw of source) { + if (!raw.data || raw.data === "[DONE]") continue + + let evt: ResponsesStreamEvent + try { + evt = JSON.parse(raw.data) as ResponsesStreamEvent + } catch { + continue + } + + if (evt.type === "response.output_text.delta" && evt.delta !== undefined) { + const chunk: ChatCompletionChunk = { + id, + object: "chat.completion.chunk", + created, + model: requestModel, + choices: [ + { + index: 0, + delta: { role: "assistant", content: evt.delta }, + finish_reason: null, + logprobs: null, + }, + ], + } + yield { data: JSON.stringify(chunk) } + } else if (evt.type === "response.completed") { + const usage = evt.response?.usage + const chunk: ChatCompletionChunk = { + id, + object: "chat.completion.chunk", + created, + model: requestModel, + choices: [ + { + index: 0, + delta: {}, + finish_reason: "stop", + logprobs: null, + }, + ], + usage: usage + ? { + prompt_tokens: usage.input_tokens, + completion_tokens: usage.output_tokens, + total_tokens: usage.total_tokens, + } + : undefined, + } + yield { data: JSON.stringify(chunk) } + } + } + + yield { data: "[DONE]" } +} + +// ---------- public entry point ---------- + +export const createChatCompletionsViaResponses = async ( + payload: ChatCompletionsPayload, +) => { + if (!state.copilotToken) throw new Error("Copilot token not found") + + const responsesPayload = toResponsesPayload(payload) + + const isAgentCall = payload.messages.some((msg) => + ["assistant", "tool"].includes(msg.role), + ) + + const headers: Record = { + ...copilotHeaders(state), + "X-Initiator": isAgentCall ? "agent" : "user", + } + + const response = await fetch(`${copilotBaseUrl(state)}/responses`, { + method: "POST", + headers, + body: JSON.stringify(responsesPayload), + }) + + if (!response.ok) { + const body = await response.text() + consola.error( + `Failed to create response via /responses [model=${payload.model}]`, + body, + ) + throw new HTTPError("Failed to create response via /responses", response, body) + } + + if (payload.stream) { + const stream = events(response) + // We need a stable id — generate one; it will be replaced once we see + // the response.created event, but for simplicity use a fixed placeholder. + return translateResponsesStream(stream, payload.model, `resp-${Date.now()}`) + } + + const data = (await response.json()) as ResponsesResponse + return responsesResponseToChatCompletion(data, payload.model) +} diff --git a/src/services/copilot/get-models.ts b/src/services/copilot/get-models.ts index 15223fac4..ec8447eb3 100644 --- a/src/services/copilot/get-models.ts +++ b/src/services/copilot/get-models.ts @@ -55,4 +55,5 @@ export interface Model { state: string terms: string } + supported_endpoints?: string[] } From 8024c9f338f6a7c893459fbe8bb064381acbdd3d Mon Sep 17 00:00:00 2001 From: David Date: Thu, 19 Mar 2026 11:16:09 +0800 Subject: [PATCH 10/13] fix: address security issues in console mode - Set file permissions to 0o600 for admin.json and accounts.json to restrict access to sensitive credential and token data - Remove githubToken from all account API responses to prevent token exposure to browser/frontend - Fix Bearer token parsing to be case-insensitive (use regex instead of string replace) - Auto-cleanup expired device flow sessions from memory after 60s - Remove .idea/ IDE files from git tracking and add to .gitignore Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 5 +- .idea/.gitignore | 10 ---- .idea/codeStyles/Project.xml | 59 -------------------- .idea/codeStyles/codeStyleConfig.xml | 5 -- .idea/copilot-api.iml | 8 --- .idea/inspectionProfiles/Project_Default.xml | 6 -- .idea/modules.xml | 8 --- .idea/vcs.xml | 6 -- src/console/account-store.ts | 2 +- src/console/admin-auth.ts | 2 +- src/console/api.ts | 22 +++++--- src/console/auth-flow.ts | 2 + 12 files changed, 23 insertions(+), 112 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/copilot-api.iml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 577a4f199..c72fab691 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,7 @@ node_modules/ .eslintcache # build output -dist/ \ No newline at end of file +dist/ + +# IDE +.idea/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b6b1ecf10..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml -# 已忽略包含查询文件的默认文件夹 -/queries/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# 基于编辑器的 HTTP 客户端请求 -/httpRequests/ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 238b56b02..000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c2..000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/copilot-api.iml b/.idea/copilot-api.iml deleted file mode 100644 index c956989b2..000000000 --- a/.idea/copilot-api.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 03d9549ea..000000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b5e6487a4..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfb..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/console/account-store.ts b/src/console/account-store.ts index 1297c6993..1ca1bd3c6 100644 --- a/src/console/account-store.ts +++ b/src/console/account-store.ts @@ -42,7 +42,7 @@ async function readStore(): Promise { } async function writeStore(store: AccountStore): Promise { - await fs.writeFile(STORE_PATH, JSON.stringify(store, null, 2)) + await fs.writeFile(STORE_PATH, JSON.stringify(store, null, 2), { mode: 0o600 }) } export async function getAccounts(): Promise> { diff --git a/src/console/admin-auth.ts b/src/console/admin-auth.ts index 03a09651e..98ddf24e4 100644 --- a/src/console/admin-auth.ts +++ b/src/console/admin-auth.ts @@ -27,7 +27,7 @@ async function readStore(): Promise { } async function writeStore(store: AdminStore): Promise { - await fs.writeFile(STORE_PATH, JSON.stringify(store, null, 2)) + await fs.writeFile(STORE_PATH, JSON.stringify(store, null, 2), { mode: 0o600 }) } export async function isSetupRequired(): Promise { diff --git a/src/console/api.ts b/src/console/api.ts index bba8c6011..9be42f327 100644 --- a/src/console/api.ts +++ b/src/console/api.ts @@ -41,6 +41,14 @@ function formatZodError(err: z.ZodError): string { : err.message } +function sanitizeAccount( + account: T, +): Omit { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { githubToken, ...rest } = account + return rest +} + export const consoleApi = new Hono() consoleApi.use(cors()) @@ -91,7 +99,7 @@ consoleApi.post("/auth/login", async (c) => { // Admin auth middleware (session token) consoleApi.use("/*", async (c, next) => { const auth = c.req.header("authorization") - const token = auth?.replace("Bearer ", "") + const token = auth?.replace(/^Bearer\s+/i, "") if (!token || !(await validateSession(token))) { return c.json({ error: "Unauthorized" }, 401) } @@ -116,7 +124,7 @@ consoleApi.get("/accounts", async (c) => { /* ignore */ } } - return { ...account, status, error, user } + return { ...sanitizeAccount(account), status, error, user } }), ) return c.json(result) @@ -158,7 +166,7 @@ consoleApi.get("/accounts/:id", async (c) => { if (!account) return c.json({ error: "Account not found" }, 404) const status = getInstanceStatus(account.id) const error = getInstanceError(account.id) - return c.json({ ...account, status, error }) + return c.json({ ...sanitizeAccount(account), status, error }) }) const AddAccountSchema = z.object({ @@ -177,7 +185,7 @@ consoleApi.post("/accounts", async (c) => { return c.json({ error: formatZodError(parsed.error) }, 400) } const account = await addAccount(parsed.data) - return c.json(account, 201) + return c.json(sanitizeAccount(account), 201) }) const UpdateAccountSchema = z.object({ @@ -197,7 +205,7 @@ consoleApi.put("/accounts/:id", async (c) => { } const account = await updateAccount(c.req.param("id"), parsed.data) if (!account) return c.json({ error: "Account not found" }, 404) - return c.json(account) + return c.json(sanitizeAccount(account)) }) // Delete account @@ -213,7 +221,7 @@ consoleApi.delete("/accounts/:id", async (c) => { consoleApi.post("/accounts/:id/regenerate-key", async (c) => { const account = await regenerateApiKey(c.req.param("id")) if (!account) return c.json({ error: "Account not found" }, 404) - return c.json(account) + return c.json(sanitizeAccount(account)) }) // Start instance @@ -291,7 +299,7 @@ consoleApi.post("/auth/complete", async (c) => { }) cleanupSession(parsed.data.sessionId) - return c.json(account, 201) + return c.json(sanitizeAccount(account), 201) }) // === Pool Configuration === diff --git a/src/console/auth-flow.ts b/src/console/auth-flow.ts index 517a5568b..eabfcabaf 100644 --- a/src/console/auth-flow.ts +++ b/src/console/auth-flow.ts @@ -86,6 +86,8 @@ async function pollForToken( session.status = "expired" session.error = "Device code expired" consola.warn(`Auth session ${sessionId} expired`) + // Schedule cleanup after a short delay so callers can still read the status + setTimeout(() => sessions.delete(sessionId), 60_000) return } From befc5064923b2e30025672982ac9d3b5e5ea0064 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 19 Mar 2026 14:27:54 +0800 Subject: [PATCH 11/13] docs: add console mode and local build instructions to README Co-Authored-By: Claude Sonnet 4.6 --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/README.md b/README.md index 7388df2c6..35b68aff6 100644 --- a/README.md +++ b/README.md @@ -376,6 +376,76 @@ bun run dev bun run start ``` +### Build from Source + +To compile the project into `dist/`: + +```sh +bun run build +``` + +Then run the compiled output: + +```sh +bun dist/main.js start +``` + +## Console Mode (Multi-Account Management) + +Console mode starts a web UI for managing multiple GitHub Copilot accounts with load balancing. + +### 1. Build the Web UI (first time only) + +```sh +cd web && bun install && bun run build +cd .. +``` + +### 2. Start Console Mode + +```sh +bun run start console --web-port 3000 --proxy-port 4141 +``` + +| Option | Description | Default | +| --- | --- | --- | +| `--web-port` / `-w` | Port for the web management console | 3000 | +| `--proxy-port` / `-p` | Port for the proxy API endpoints | 4141 | +| `--auto-start` | Auto-start enabled accounts on launch | true | +| `--verbose` / `-v` | Enable verbose logging | false | + +### 3. Set Up Admin Account + +Open `http://localhost:3000` in your browser. On first visit you will be prompted to create an admin username and password (minimum 6 characters). + +### 4. Add Accounts + +Click **Add Account** in the dashboard and authenticate via GitHub Device Flow, or paste a GitHub token directly. + +### 5. Using the API Key + +After adding and starting an account, each account has its own **API Key** shown on its card. + +**Single-account mode** (use a specific account's key): + +``` +Base URL: http://localhost:4141/v1 +API Key: +``` + +**Pool mode** (load-balance across all enabled accounts): + +Enable Pool in the dashboard to get a shared API Key. Choose a strategy: +- `round-robin` — distribute requests evenly across accounts +- `priority` — route to the highest-priority available account + +``` +Base URL: http://localhost:4141/v1 +API Key: +``` + +Both keys are used as a standard Bearer token. They work with any OpenAI-compatible or Anthropic-compatible client — just point the client's base URL to `http://localhost:4141/v1` and set the API key accordingly. + ## Usage Tips - To avoid hitting GitHub Copilot's rate limits, you can use the following flags: From ff1d29ac02e4e579866352e22aec639a7ef1a497 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 20 Mar 2026 10:50:48 +0800 Subject: [PATCH 12/13] Update README with Claude setup instructions Added important commands for Claude code setup. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 35b68aff6..08cd12943 100644 --- a/README.md +++ b/README.md @@ -453,3 +453,13 @@ Both keys are used as a standard Bearer token. They work with any OpenAI-compati - `--rate-limit `: Enforces a minimum time interval between requests. For example, `copilot-api start --rate-limit 30` will ensure there's at least a 30-second gap between requests. - `--wait`: Use this with `--rate-limit`. It makes the server wait for the cooldown period to end instead of rejecting the request with an error. This is useful for clients that don't automatically retry on rate limit errors. - If you have a GitHub business or enterprise plan account with Copilot, use the `--account-type` flag (e.g., `--account-type business`). See the [official documentation](https://docs.github.com/en/enterprise-cloud@latest/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-access-to-github-copilot-in-your-organization/managing-github-copilot-access-to-your-organizations-network#configuring-copilot-subscription-based-network-routing-for-your-enterprise-or-organization) for more details. + + +## Important commands with Claude code + +curl -fsSL https://bun.sh/install | bash +cd web && bun install && bun run build +cd .. +bun run start console --web-port 3000 --proxy-port 4141 +vi ~/.claude/settings.json +set api key in settings.json with the key copied in http://localhost:3000 From 32cae0359e2469f22ef59d82cd38428271e58979 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 20 Mar 2026 10:51:27 +0800 Subject: [PATCH 13/13] Fix formatting in Important commands section --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 08cd12943..38f6d27b9 100644 --- a/README.md +++ b/README.md @@ -457,9 +457,9 @@ Both keys are used as a standard Bearer token. They work with any OpenAI-compati ## Important commands with Claude code -curl -fsSL https://bun.sh/install | bash -cd web && bun install && bun run build -cd .. -bun run start console --web-port 3000 --proxy-port 4141 -vi ~/.claude/settings.json -set api key in settings.json with the key copied in http://localhost:3000 +curl -fsSL https://bun.sh/install | bash +cd web && bun install && bun run build +cd .. +bun run start console --web-port 3000 --proxy-port 4141 +vi ~/.claude/settings.json +set api key in settings.json with the key copied in http://localhost:3000