From 04e01842b289c6bce6a9336da135d29ff73f53b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 12:36:23 +0800 Subject: [PATCH 01/20] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/i18n/locales/en.ts | 11 + frontend/src/i18n/locales/zh.ts | 11 + .../__tests__/configScriptDownload.spec.ts | 60 ++++ frontend/src/utils/configScriptDownload.ts | 302 ++++++++++++++++++ frontend/src/views/user/KeysView.vue | 160 ++++++++++ 5 files changed, 544 insertions(+) create mode 100644 frontend/src/utils/__tests__/configScriptDownload.spec.ts create mode 100644 frontend/src/utils/configScriptDownload.ts diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 1ad34b0e65b..df1f969bbc7 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -701,6 +701,7 @@ export default { created: 'Created', copyToClipboard: 'Copy to clipboard', copied: 'Copied!', + configScript: 'Config Script', importToCcSwitch: 'Import to CCS', enable: 'Enable', disable: 'Disable', @@ -732,6 +733,16 @@ export default { quota: 'Quota', lastUsedAt: 'Last Used', useKey: 'Use Key', + configScriptMenu: { + codexCli: 'Codex CLI', + claudeCode: 'Claude Code', + opencode: 'OpenCode', + hintMac: 'macOS downloads .sh. Run sh ~/Downloads/script-name.sh in terminal, or chmod +x before running.', + hintWindows: 'Windows downloads .bat. Double-click it or run it in CMD after downloading.', + unsupportedClient: 'This group does not support the selected client config script', + downloadStarted: 'Config script download started', + downloadFailed: 'Failed to download config script' + }, useKeyModal: { title: 'Use API Key', description: diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 2868e5d5981..eef7b3c1528 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -700,6 +700,7 @@ export default { created: '创建时间', copyToClipboard: '复制到剪贴板', copied: '已复制!', + configScript: '配置脚本', importToCcSwitch: '导入到 CCS', enable: '启用', disable: '禁用', @@ -731,6 +732,16 @@ export default { quota: '额度', lastUsedAt: '上次使用时间', useKey: '使用密钥', + configScriptMenu: { + codexCli: 'Codex CLI', + claudeCode: 'Claude Code', + opencode: 'OpenCode', + hintMac: 'macOS 下载 .sh;建议在终端执行 sh ~/Downloads/脚本名.sh,或 chmod +x 后运行。', + hintWindows: 'Windows 下载 .bat;下载后双击或在 CMD 中运行。', + unsupportedClient: '当前分组不支持此客户端配置脚本', + downloadStarted: '配置脚本已开始下载', + downloadFailed: '下载配置脚本失败' + }, useKeyModal: { title: '使用 API 密钥', description: '将以下环境变量添加到您的终端配置文件或直接在终端中运行。', diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts new file mode 100644 index 00000000000..496f36fa272 --- /dev/null +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest' +import { + buildAPIKeyConfigScript, + isConfigScriptClientAvailable +} from '../configScriptDownload' + +function decodeBatchPayload(content: string): string { + const encoded = content.match(/FromBase64String\('([^']+)'\)/)?.[1] + if (!encoded) throw new Error('missing encoded payload') + const binary = atob(encoded) + const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)) + return new TextDecoder().decode(bytes) +} + +describe('configScriptDownload', () => { + it('builds a macOS Codex shell script with look2eye config values', () => { + const script = buildAPIKeyConfigScript({ + client: 'codex', + os: 'mac', + platform: 'openai', + baseUrl: 'https://api.example.com/v1', + apiKey: 'sk-test-key' + }) + + expect(script.filename).toBe('look2eye-codex-config.sh') + expect(script.content).toContain('#!/usr/bin/env sh') + expect(script.content).toContain('Installing look2eye Codex CLI configuration') + expect(script.content).toContain('base_url = "https://api.example.com/v1"') + expect(script.content).toContain('"OPENAI_API_KEY": "sk-test-key"') + expect(script.content).toContain('$HOME/.codex/config.toml') + }) + + it('builds a Windows Claude Code batch script with replaced API node and key', () => { + const script = buildAPIKeyConfigScript({ + client: 'claude', + os: 'win', + platform: 'antigravity', + baseUrl: 'https://api.example.com/v1', + apiKey: 'sk-claude-key' + }) + + expect(script.filename).toBe('look2eye-claude-code-config.bat') + expect(script.content).toContain('@echo off') + expect(script.content).toContain('Installing look2eye Claude Code configuration') + + const payload = decodeBatchPayload(script.content) + expect(payload).toContain('Installing look2eye Claude Code configuration') + expect(payload).toContain('https://api.example.com/antigravity/v1') + expect(payload).toContain('sk-claude-key') + expect(payload).toContain("ANTHROPIC_AUTH_TOKEN") + }) + + it('limits client availability by platform', () => { + expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'openai' })).toBe(true) + expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'gemini' })).toBe(false) + expect(isConfigScriptClientAvailable({ client: 'claude', platform: 'openai' })).toBe(false) + expect(isConfigScriptClientAvailable({ client: 'claude', platform: 'openai', allowMessagesDispatch: true })).toBe(true) + expect(isConfigScriptClientAvailable({ client: 'opencode', platform: 'gemini' })).toBe(true) + }) +}) diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts new file mode 100644 index 00000000000..1e350268229 --- /dev/null +++ b/frontend/src/utils/configScriptDownload.ts @@ -0,0 +1,302 @@ +import type { GroupPlatform } from '@/types' + +export const CONFIG_SCRIPT_SITE_NAME = 'look2eye' + +export type ConfigScriptClient = 'codex' | 'claude' | 'opencode' +export type ConfigScriptOS = 'mac' | 'win' + +export interface ConfigScriptInput { + client: ConfigScriptClient + os?: ConfigScriptOS + platform?: GroupPlatform | null + baseUrl: string + apiKey: string + siteName?: string + allowMessagesDispatch?: boolean +} + +interface ConfigFile { + path: string + content: string +} + +interface ConfigPayload { + label: string + files: ConfigFile[] + env?: Record +} + +const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '') + +const stripV1Suffix = (value: string) => trimTrailingSlash(value).replace(/\/v1\/?$/, '') + +const ensureSuffix = (value: string, suffix: string) => { + const trimmed = trimTrailingSlash(value) + return trimmed.endsWith(suffix) ? trimmed : `${trimmed}${suffix}` +} + +const resolveBaseUrl = (baseUrl: string) => trimTrailingSlash(baseUrl || window.location.origin) + +const resolveAPIBase = (baseUrl: string) => ensureSuffix(stripV1Suffix(resolveBaseUrl(baseUrl)), '/v1') + +const resolveGeminiBase = (baseUrl: string) => ensureSuffix(stripV1Suffix(resolveBaseUrl(baseUrl)), '/v1beta') + +const resolveAntigravityBase = (baseUrl: string) => + ensureSuffix(`${stripV1Suffix(resolveBaseUrl(baseUrl))}/antigravity`, '/v1') + +function shellQuote(value: string): string { + return `'${value.replace(/'/g, `'\\''`)}'` +} + +function json(value: unknown): string { + return JSON.stringify(value, null, 2) +} + +function buildCodexPayload(input: Required>): ConfigPayload { + const configToml = `# ${input.siteName} Codex CLI configuration +model_provider = "OpenAI" +model = "gpt-5.5" +review_model = "gpt-5.5" +model_reasoning_effort = "xhigh" +disable_response_storage = true +network_access = "enabled" +windows_wsl_setup_acknowledged = true + +[model_providers.OpenAI] +name = "OpenAI" +base_url = "${resolveBaseUrl(input.baseUrl)}" +wire_api = "responses" +requires_openai_auth = true + +[features] +goals = true` + + const authJSON = json({ + OPENAI_API_KEY: input.apiKey + }) + + return { + label: 'Codex CLI', + files: [ + { path: '.codex/config.toml', content: configToml }, + { path: '.codex/auth.json', content: authJSON } + ] + } +} + +function buildClaudePayload(input: Required> & Pick): ConfigPayload { + const baseUrl = input.platform === 'antigravity' + ? resolveAntigravityBase(input.baseUrl) + : resolveBaseUrl(input.baseUrl) + const env = { + ANTHROPIC_BASE_URL: baseUrl, + ANTHROPIC_AUTH_TOKEN: input.apiKey, + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1', + CLAUDE_CODE_ATTRIBUTION_HEADER: '0' + } + const envContent = `export ANTHROPIC_BASE_URL=${shellQuote(baseUrl)} +export ANTHROPIC_AUTH_TOKEN=${shellQuote(input.apiKey)} +export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 +export CLAUDE_CODE_ATTRIBUTION_HEADER=0` + const settingsJSON = json({ + env + }) + + return { + label: 'Claude Code', + env, + files: [ + { path: '.look2eye/claude-code.env', content: `# ${input.siteName} Claude Code environment\n${envContent}\n` }, + { path: '.claude/settings.json', content: settingsJSON } + ] + } +} + +function buildOpenCodePayload(input: Required> & Pick): ConfigPayload { + const platform = input.platform || 'anthropic' + const providerID = platform === 'antigravity' ? 'antigravity-claude' : platform + const baseURL = platform === 'gemini' + ? resolveGeminiBase(input.baseUrl) + : platform === 'antigravity' + ? resolveAntigravityBase(input.baseUrl) + : resolveAPIBase(input.baseUrl) + const model = platform === 'gemini' + ? 'gemini-2.0-flash' + : platform === 'openai' + ? 'gpt-5.5' + : 'claude-opus-4-6-thinking' + + const config = { + $schema: 'https://opencode.ai/config.json', + provider: { + [providerID]: { + npm: platform === 'gemini' ? '@ai-sdk/google' : platform === 'openai' ? '@ai-sdk/openai' : '@ai-sdk/anthropic', + name: input.siteName, + options: { + baseURL, + apiKey: input.apiKey + }, + models: { + [model]: { + name: model + } + } + } + } + } + + return { + label: 'OpenCode', + files: [ + { path: '.config/opencode/opencode.json', content: json(config) } + ] + } +} + +function buildPayload(input: ConfigScriptInput): ConfigPayload { + const base = { + baseUrl: input.baseUrl, + apiKey: input.apiKey, + siteName: input.siteName || CONFIG_SCRIPT_SITE_NAME + } + + switch (input.client) { + case 'codex': + return buildCodexPayload(base) + case 'claude': + return buildClaudePayload({ ...base, platform: input.platform }) + case 'opencode': + return buildOpenCodePayload({ ...base, platform: input.platform }) + } +} + +function buildShellScript(payload: ConfigPayload, siteName: string): string { + const writeFileCommands = payload.files.map((file) => { + const target = `$HOME/${file.path}` + const dir = file.path.split('/').slice(0, -1).join('/') + return `mkdir -p "$HOME/${dir}" +cat > "${target}" <<'LOOK2EYE_CONFIG_EOF' +${file.content} +LOOK2EYE_CONFIG_EOF` + }).join('\n\n') + + const profileCommands = payload.files.some((file) => file.path === '.look2eye/claude-code.env') + ? ` +PROFILE="$HOME/.zshrc" +if [ -n "\${BASH_VERSION:-}" ]; then + PROFILE="$HOME/.bashrc" +fi +touch "$PROFILE" +SOURCE_LINE='. "$HOME/.look2eye/claude-code.env"' +if ! grep -qxF "$SOURCE_LINE" "$PROFILE"; then + printf '\\n# ${siteName} Claude Code\\n%s\\n' "$SOURCE_LINE" >> "$PROFILE" +fi` + : '' + + return `#!/usr/bin/env sh +set -eu + +echo "Installing ${siteName} ${payload.label} configuration..." + +${writeFileCommands}${profileCommands} + +echo "Done. Restart your terminal or source your shell profile if environment variables were updated." +` +} + +function buildPowerShellScript(payload: ConfigPayload, siteName: string): string { + const writeFileCommands = payload.files.map((file) => { + const windowsPath = file.path.replace(/\//g, '\\') + return `$target = Join-Path $env:USERPROFILE ${JSON.stringify(windowsPath)} +New-Item -ItemType Directory -Force -Path (Split-Path $target) | Out-Null +@' +${file.content} +'@ | Set-Content -Encoding UTF8 -Path $target` + }).join('\n\n') + + const envCommands = payload.env + ? Object.entries(payload.env) + .map(([key, value]) => `[Environment]::SetEnvironmentVariable(${JSON.stringify(key)}, ${JSON.stringify(value)}, 'User')`) + .join('\n') + : '' + + return `$ErrorActionPreference = 'Stop' +Write-Host "Installing ${siteName} ${payload.label} configuration..." + +${writeFileCommands}${envCommands ? `\n\n${envCommands}` : ''} + +Write-Host "Done. Restart your terminal for environment variable changes to take effect." +` +} + +function toBase64UTF8(value: string): string { + const bytes = new TextEncoder().encode(value) + let binary = '' + bytes.forEach((byte) => { + binary += String.fromCharCode(byte) + }) + return btoa(binary) +} + +function buildBatchScript(payload: ConfigPayload, siteName: string): string { + const psScript = buildPowerShellScript(payload, siteName) + const encoded = toBase64UTF8(psScript) + return `@echo off +setlocal +echo Installing ${siteName} ${payload.label} configuration... +powershell -NoProfile -ExecutionPolicy Bypass -Command "$script = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); $path = Join-Path $env:TEMP 'look2eye-config.ps1'; Set-Content -Encoding UTF8 -Path $path -Value $script; & $path" +if errorlevel 1 ( + echo Installation failed. + pause + exit /b 1 +) +echo Done. +pause +` +} + +export function getConfigScriptOS(): ConfigScriptOS { + const nav = navigator as Navigator & { userAgentData?: { platform?: string } } + const platform = nav.userAgentData?.platform || navigator.platform || '' + return /win/i.test(platform) ? 'win' : 'mac' +} + +export function buildAPIKeyConfigScript(input: ConfigScriptInput): { filename: string; content: string; os: ConfigScriptOS } { + const os = input.os || getConfigScriptOS() + const siteName = input.siteName || CONFIG_SCRIPT_SITE_NAME + const payload = buildPayload({ ...input, siteName }) + const filenameClient = input.client === 'claude' ? 'claude-code' : input.client + const extension = os === 'win' ? 'bat' : 'sh' + const content = os === 'win' ? buildBatchScript(payload, siteName) : buildShellScript(payload, siteName) + + return { + filename: `${siteName}-${filenameClient}-config.${extension}`, + content, + os + } +} + +export function isConfigScriptClientAvailable(input: Pick): boolean { + switch (input.client) { + case 'codex': + return input.platform === 'openai' + case 'claude': + return input.platform === 'anthropic' || input.platform === 'antigravity' || (input.platform === 'openai' && input.allowMessagesDispatch === true) + case 'opencode': + return !!input.platform + } +} + +export function downloadAPIKeyConfigScript(input: ConfigScriptInput): void { + const script = buildAPIKeyConfigScript(input) + const mime = script.os === 'win' ? 'application/x-bat' : 'text/x-shellscript' + const blob = new Blob([script.content], { type: `${mime};charset=utf-8` }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = script.filename + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) +} diff --git a/frontend/src/views/user/KeysView.vue b/frontend/src/views/user/KeysView.vue index 7ca0a1c7877..2b68cc42cb3 100644 --- a/frontend/src/views/user/KeysView.vue +++ b/frontend/src/views/user/KeysView.vue @@ -311,6 +311,20 @@ @@ -1077,6 +1122,12 @@ import { buildCcSwitchImportDeeplink, type CcSwitchClientType } from '@/utils/ccswitchImport' +import { + downloadAPIKeyConfigScript, + getConfigScriptOS, + isConfigScriptClientAvailable, + type ConfigScriptClient +} from '@/utils/configScriptDownload' // Helper to format date for datetime-local input const formatDateTimeLocal = (isoDate: string): string => { @@ -1148,10 +1199,14 @@ const pendingCcsRow = ref(null) const selectedKey = ref(null) const copiedKeyId = ref(null) const groupSelectorKeyId = ref(null) +const configScriptMenuKeyId = ref(null) const publicSettings = ref(null) const dropdownRef = ref(null) const dropdownPosition = ref<{ top?: number; bottom?: number; left: number } | null>(null) const groupButtonRefs = ref>(new Map()) +const configScriptMenuRef = ref(null) +const configScriptMenuPosition = ref<{ top: number; left: number } | null>(null) +const configScriptButtonRefs = ref>(new Map()) let abortController: AbortController | null = null // Get the currently selected key for group change @@ -1160,6 +1215,11 @@ const selectedKeyForGroup = computed(() => { return apiKeys.value.find((k) => k.id === groupSelectorKeyId.value) || null }) +const selectedKeyForConfigScript = computed(() => { + if (configScriptMenuKeyId.value === null) return null + return apiKeys.value.find((k) => k.id === configScriptMenuKeyId.value) || null +}) + const setGroupButtonRef = (keyId: number, el: Element | ComponentPublicInstance | null) => { if (el instanceof HTMLElement) { groupButtonRefs.value.set(keyId, el) @@ -1168,6 +1228,14 @@ const setGroupButtonRef = (keyId: number, el: Element | ComponentPublicInstance } } +const setConfigScriptButtonRef = (keyId: number, el: Element | ComponentPublicInstance | null) => { + if (el instanceof HTMLElement) { + configScriptButtonRefs.value.set(keyId, el) + } else { + configScriptButtonRefs.value.delete(keyId) + } +} + const formData = ref({ name: '', group_id: null as number | null, @@ -1226,6 +1294,18 @@ const statusFilterOptions = computed(() => [ { value: 'expired', label: t('keys.status.expired') } ]) +const configScriptClientItems = computed(() => [ + { id: 'codex' as ConfigScriptClient, label: t('keys.configScriptMenu.codexCli') }, + { id: 'claude' as ConfigScriptClient, label: t('keys.configScriptMenu.claudeCode') }, + { id: 'opencode' as ConfigScriptClient, label: t('keys.configScriptMenu.opencode') } +]) + +const configScriptDownloadHint = computed(() => + getConfigScriptOS() === 'win' + ? t('keys.configScriptMenu.hintWindows') + : t('keys.configScriptMenu.hintMac') +) + const onFilterChange = () => { pagination.value.page = 1 loadApiKeys() @@ -1427,6 +1507,7 @@ const toggleKeyStatus = async (key: ApiKey) => { } const openGroupSelector = (key: ApiKey) => { + closeConfigScriptMenu() if (groupSelectorKeyId.value === key.id) { groupSelectorKeyId.value = null dropdownPosition.value = null @@ -1457,6 +1538,78 @@ const openGroupSelector = (key: ApiKey) => { } } +const closeConfigScriptMenu = () => { + configScriptMenuKeyId.value = null + configScriptMenuPosition.value = null +} + +const openConfigScriptMenu = (key: ApiKey) => { + groupSelectorKeyId.value = null + dropdownPosition.value = null + + if (configScriptMenuKeyId.value === key.id) { + closeConfigScriptMenu() + return + } + + const buttonEl = configScriptButtonRefs.value.get(key.id) + if (!buttonEl) return + + const rect = buttonEl.getBoundingClientRect() + const padding = 12 + const menuHeight = 280 + const viewportWidth = window.innerWidth + const viewportHeight = window.innerHeight + const menuWidth = Math.max(0, Math.min(450, viewportWidth - padding * 2)) + let left = rect.left + rect.width / 2 - menuWidth / 2 + left = Math.max(padding, Math.min(left, viewportWidth - menuWidth - padding)) + let top = rect.bottom + 8 + if (top + menuHeight > viewportHeight - padding && rect.top > menuHeight) { + top = rect.top - menuHeight - 8 + } + top = Math.max(padding, Math.min(top, viewportHeight - menuHeight - padding)) + + configScriptMenuPosition.value = { top, left } + configScriptMenuKeyId.value = key.id +} + +const isConfigScriptMenuItemAvailable = (client: ConfigScriptClient) => { + const key = selectedKeyForConfigScript.value + if (!key?.group?.platform) return false + return isConfigScriptClientAvailable({ + client, + platform: key.group.platform, + allowMessagesDispatch: key.group.allow_messages_dispatch || false + }) +} + +const downloadConfigScript = (client: ConfigScriptClient) => { + const key = selectedKeyForConfigScript.value + if (!key?.group?.platform) { + appStore.showError(t('keys.useKeyModal.noGroupTitle')) + return + } + if (!isConfigScriptMenuItemAvailable(client)) { + appStore.showError(t('keys.configScriptMenu.unsupportedClient')) + return + } + + try { + downloadAPIKeyConfigScript({ + client, + platform: key.group.platform, + allowMessagesDispatch: key.group.allow_messages_dispatch || false, + baseUrl: publicSettings.value?.api_base_url || window.location.origin, + apiKey: key.key + }) + appStore.showSuccess(t('keys.configScriptMenu.downloadStarted')) + closeConfigScriptMenu() + } catch (error) { + console.error('Failed to download config script:', error) + appStore.showError(t('keys.configScriptMenu.downloadFailed')) + } +} + const changeGroup = async (key: ApiKey, newGroupId: number | null) => { groupSelectorKeyId.value = null dropdownPosition.value = null @@ -1478,6 +1631,13 @@ const closeGroupSelector = (event: MouseEvent) => { groupSelectorKeyId.value = null dropdownPosition.value = null } + if ( + !target.closest('.config-script-trigger') && + !target.closest('.config-script-menu-content') && + !configScriptMenuRef.value?.contains(target) + ) { + closeConfigScriptMenu() + } } const confirmDelete = (key: ApiKey) => { From 2d47d06b594a343de6cca67d8fad0735c6e0e955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 13:38:21 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..249ce8c969c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## Unreleased + +- Added a config script download action to each API key row, matching the existing API key action area. +- Added a config script dropdown with Codex CLI, Claude Code, and OpenCode options, styled after the provided reference image. +- Added automatic OS detection so macOS downloads `.sh` scripts and Windows downloads `.bat` scripts. +- Added config script generation for Codex CLI, Claude Code, and OpenCode with the current API endpoint and API key injected into the generated files. +- Set the generated script site name to `look2eye`. +- Added Chinese and English i18n text for the config script button, menu, hints, and download states. +- Added focused unit coverage for config script generation and client availability rules. From 235b7511ef387077ceb334a7f27d6b890e9ba789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 15:37:43 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/configScriptDownload.spec.ts | 94 ++- frontend/src/utils/configScriptDownload.ts | 789 +++++++++++++++++- frontend/src/views/user/KeysView.vue | 3 +- 3 files changed, 841 insertions(+), 45 deletions(-) diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index 496f36fa272..66c987b0e80 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -12,6 +12,13 @@ function decodeBatchPayload(content: string): string { return new TextDecoder().decode(bytes) } +function extractClaudeBatchPayload(content: string): string { + const marker = '__PINAI_CLAUDE_CODE_PS1__' + const markerIndex = content.lastIndexOf(marker) + if (markerIndex < 0) throw new Error('missing Claude Code payload marker') + return content.slice(markerIndex + marker.length).trimStart() +} + describe('configScriptDownload', () => { it('builds a macOS Codex shell script with look2eye config values', () => { const script = buildAPIKeyConfigScript({ @@ -24,13 +31,68 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-codex-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') - expect(script.content).toContain('Installing look2eye Codex CLI configuration') - expect(script.content).toContain('base_url = "https://api.example.com/v1"') - expect(script.content).toContain('"OPENAI_API_KEY": "sk-test-key"') - expect(script.content).toContain('$HOME/.codex/config.toml') + expect(script.content).toContain('look2eye Codex CLI 一键配置') + expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") + expect(script.content).toContain("API_KEY='sk-test-key'") + expect(script.content).toContain('CONFIG_PATH="$CODEX_DIR/config.toml"') + expect(script.content).toContain('AUTH_PATH="$CODEX_DIR/auth.json"') + expect(script.content).toContain('BACKUP_DIR="$CODEX_DIR/backups"') + expect(script.content).toContain('restore|--restore|/restore') + expect(script.content).toContain('base_url = "{{PINAI_BASE_URL}}"') + expect(script.content).toContain('json.dumps({"OPENAI_API_KEY": api_key}') + expect(script.content).toContain('stop_codex_processes') }) - it('builds a Windows Claude Code batch script with replaced API node and key', () => { + it('builds a Windows Codex batch script with embedded setup payload', () => { + const script = buildAPIKeyConfigScript({ + client: 'codex', + os: 'win', + platform: 'openai', + baseUrl: 'https://api.example.com/v1', + apiKey: 'sk-win-key', + siteName: 'PinAI' + }) + + expect(script.filename).toBe('PinAI-codex-config.bat') + expect(script.content).toContain('@echo off') + expect(script.content).toContain('PINAI_CODEX_EXIT') + + const payload = decodeBatchPayload(script.content) + expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") + expect(payload).toContain("$ApiKey = 'sk-win-key'") + expect(payload).toContain('$ConfigTomlTemplate') + expect(payload).toContain('model_provider = "PinAI"') + expect(payload).toContain('Restore-CodexBackup') + expect(payload).toContain('Stop-CodexProcesses') + }) + + it('builds a macOS Claude Code shell script that merges settings.json', () => { + const script = buildAPIKeyConfigScript({ + client: 'claude', + os: 'mac', + platform: 'anthropic', + baseUrl: 'https://api.example.com/v1/', + apiKey: 'sk-claude-key', + siteName: 'PinAI' + }) + + expect(script.filename).toBe('PinAI-claude-code-config.sh') + expect(script.content).toContain('#!/usr/bin/env sh') + expect(script.content).toContain('PinAI Claude Code 配置已完成') + expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") + expect(script.content).toContain("API_KEY='sk-claude-key'") + expect(script.content).toContain('CLAUDE_DIR="$HOME/.claude"') + expect(script.content).toContain('SETTINGS_PATH="$CLAUDE_DIR/settings.json"') + expect(script.content).toContain('BACKUP_DIR="$CLAUDE_DIR/backups"') + expect(script.content).toContain('settings = json.loads') + expect(script.content).toContain('"ANTHROPIC_BASE_URL": base_url') + expect(script.content).toContain('"ANTHROPIC_AUTH_TOKEN": api_key') + expect(script.content).toContain('"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"') + expect(script.content).toContain('"CLAUDE_CODE_ATTRIBUTION_HEADER": "0"') + expect(script.content).not.toContain('.look2eye/claude-code.env') + }) + + it('builds a Windows Claude Code batch script from the marker-based template', () => { const script = buildAPIKeyConfigScript({ client: 'claude', os: 'win', @@ -41,19 +103,27 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-claude-code-config.bat') expect(script.content).toContain('@echo off') - expect(script.content).toContain('Installing look2eye Claude Code configuration') + expect(script.content).toContain('PINAI_SETUP_MARKER=__PINAI_CLAUDE_CODE_PS1__') + expect(script.content).toContain('__PINAI_CLAUDE_CODE_PS1__') + expect(script.content).toContain('PINAI_SETUP_LAUNCHED_FROM_EXPLORER') - const payload = decodeBatchPayload(script.content) - expect(payload).toContain('Installing look2eye Claude Code configuration') + const payload = extractClaudeBatchPayload(script.content) + expect(payload).toContain("Applying $SiteName Claude Code config") expect(payload).toContain('https://api.example.com/antigravity/v1') expect(payload).toContain('sk-claude-key') - expect(payload).toContain("ANTHROPIC_AUTH_TOKEN") + expect(payload).toContain('ConvertTo-OrderedMap') + expect(payload).toContain('Join-Path $targetHome ".claude"') + expect(payload).toContain('Join-Path $claudeDir "settings.json"') + expect(payload).toContain('Join-Path $claudeDir "backups"') + expect(payload).toContain('$envMap["ANTHROPIC_AUTH_TOKEN"] = $ApiKey') + expect(payload).toContain('$envMap["CLAUDE_CODE_ATTRIBUTION_HEADER"] = "0"') + expect(payload).toContain('Write-Utf8NoBom') }) - it('limits client availability by platform', () => { + it('allows config scripts for every grouped platform', () => { expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'openai' })).toBe(true) - expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'gemini' })).toBe(false) - expect(isConfigScriptClientAvailable({ client: 'claude', platform: 'openai' })).toBe(false) + expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'gemini' })).toBe(true) + expect(isConfigScriptClientAvailable({ client: 'claude', platform: 'openai' })).toBe(true) expect(isConfigScriptClientAvailable({ client: 'claude', platform: 'openai', allowMessagesDispatch: true })).toBe(true) expect(isConfigScriptClientAvailable({ client: 'opencode', platform: 'gemini' })).toBe(true) }) diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index 1e350268229..116e9828614 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -26,6 +26,12 @@ interface ConfigPayload { env?: Record } +const CODEX_DEFAULT_MODEL = 'gpt-5.5' +const CODEX_DEFAULT_REVIEW_MODEL = 'gpt-5.5' +const CODEX_REASONING_EFFORT = 'xhigh' +const CODEX_CONTEXT_WINDOW = 1000000 +const CODEX_AUTO_COMPACT_TOKEN_LIMIT = 900000 + const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '') const stripV1Suffix = (value: string) => trimTrailingSlash(value).replace(/\/v1\/?$/, '') @@ -48,46 +54,763 @@ function shellQuote(value: string): string { return `'${value.replace(/'/g, `'\\''`)}'` } +function powershellSingleQuote(value: string): string { + return `'${value.replace(/'/g, "''")}'` +} + function json(value: unknown): string { return JSON.stringify(value, null, 2) } -function buildCodexPayload(input: Required>): ConfigPayload { - const configToml = `# ${input.siteName} Codex CLI configuration -model_provider = "OpenAI" -model = "gpt-5.5" -review_model = "gpt-5.5" -model_reasoning_effort = "xhigh" +function codexProviderName(siteName: string): string { + const sanitized = siteName.replace(/[^A-Za-z0-9_-]+/g, '') + return sanitized || 'PinAI' +} + +function codexConfigTemplate(input: Required>): string { + const providerName = codexProviderName(input.siteName) + return `model_provider = "${providerName}" +model = "${CODEX_DEFAULT_MODEL}" +review_model = "${CODEX_DEFAULT_REVIEW_MODEL}" +model_reasoning_effort = "${CODEX_REASONING_EFFORT}" +model_context_window = ${CODEX_CONTEXT_WINDOW} +model_auto_compact_token_limit = ${CODEX_AUTO_COMPACT_TOKEN_LIMIT} disable_response_storage = true network_access = "enabled" windows_wsl_setup_acknowledged = true -[model_providers.OpenAI] -name = "OpenAI" -base_url = "${resolveBaseUrl(input.baseUrl)}" +[agents] +max_threads = 20 +max_depth = 1 + +[model_providers.${providerName}] +name = "${providerName}" +base_url = "{{PINAI_BASE_URL}}" wire_api = "responses" -requires_openai_auth = true +supports_websockets = {{PINAI_ENABLE_WEBSOCKET}} [features] -goals = true` +responses_websockets_v2 = {{PINAI_ENABLE_WEBSOCKET}} +goals = true + +[windows] +sandbox = "elevated" +` +} + +function buildCodexShellScript(input: Required>): string { + const siteName = input.siteName + const baseUrl = resolveBaseUrl(input.baseUrl) + const template = codexConfigTemplate(input) + + return `#!/usr/bin/env sh +set -eu + +pinai_setup_result() { + rc=$? + if [ "$rc" -eq 0 ]; then + printf '\\n' + echo "============================================================" + echo "[PINAI SETUP SUCCESS] ${siteName} Codex CLI 配置已完成。" + echo "请重新打开 Codex 或对应客户端,让新配置生效。" + echo "============================================================" + else + printf '\\n' >&2 + echo "============================================================" >&2 + echo "[PINAI SETUP FAILED] ${siteName} Codex CLI 配置未完成或未完全生效。退出码:$rc" >&2 + echo "请查看上方失败原因;如提示 Codex 未关闭,请手动关闭后重试。" >&2 + echo "============================================================" >&2 + fi + trap - EXIT + exit "$rc" +} +trap pinai_setup_result EXIT + +BASE_URL=${shellQuote(baseUrl)} +API_KEY=${shellQuote(input.apiKey)} +ENABLE_WEBSOCKET='false' +CONFIG_TOML_TEMPLATE=${shellQuote(template)} +CODEX_DIR="$HOME/.codex" +CONFIG_PATH="$CODEX_DIR/config.toml" +AUTH_PATH="$CODEX_DIR/auth.json" +BACKUP_DIR="$CODEX_DIR/backups" + +mkdir -p "$CODEX_DIR" "$BACKUP_DIR" + +stop_codex_processes() { + if [ "\${PINAI_SKIP_CODEX_PROCESS_CLOSE:-}" = "1" ]; then + echo "已跳过结束 Codex 进程。" + return 0 + fi + + if ! command -v pkill >/dev/null 2>&1; then + echo "配置已写入,但未找到 pkill,无法自动结束 Codex 进程。请手动关闭 Codex 后重新打开。" >&2 + return 1 + fi + + stopped=0 + failed=0 + for process_name in codex Codex; do + count=0 + if command -v pgrep >/dev/null 2>&1; then + count=$(pgrep -x "$process_name" 2>/dev/null | wc -l | tr -d ' ') + fi + if pkill -x "$process_name" >/dev/null 2>&1; then + if [ "\${count:-0}" -gt 0 ]; then + stopped=$((stopped + count)) + else + stopped=$((stopped + 1)) + fi + elif [ "\${count:-0}" -gt 0 ]; then + failed=1 + fi + done + + if [ "$stopped" -gt 0 ]; then + echo "已结束 Codex 进程:$stopped 个。" + else + echo "未发现正在运行的 Codex 进程;重新打开 Codex 即可使用新配置。" + fi + if [ "$failed" -ne 0 ]; then + echo "配置已写入,但部分 Codex 进程无法自动关闭。请手动关闭 Codex 后重新打开。" >&2 + return 1 + fi + return 0 +} + +new_backup_stamp() { + base=$(date +"%Y%m%d-%H%M%S") + stamp="$base" + counter=2 + while [ -e "$BACKUP_DIR/$(basename "$CONFIG_PATH").bak-$stamp" ] || [ -e "$BACKUP_DIR/$(basename "$AUTH_PATH").bak-$stamp" ]; do + stamp="$base-$(printf "%02d" "$counter")" + counter=$((counter + 1)) + done + printf '%s\\n' "$stamp" +} + +PYTHON_BIN= +if command -v python3 >/dev/null 2>&1 && python3 -c 'import sys' >/dev/null 2>&1; then + PYTHON_BIN=python3 +elif command -v python >/dev/null 2>&1 && python -c 'import sys' >/dev/null 2>&1; then + PYTHON_BIN=python +else + echo "失败:需要可用的 python3 或 python 才能安全合并或恢复现有 Codex 配置。" >&2 + exit 1 +fi + +PINAI_ACTION=\${1:-apply} + +case "$PINAI_ACTION" in + ""|1|apply|--apply) + printf "\\n${siteName} Codex CLI 一键配置:默认覆盖当前配置。\\n" + echo "如需恢复备份,请在终端运行本脚本并追加 restore 参数。" + ;; + 2|restore|--restore|/restore) + "$PYTHON_BIN" - "$CONFIG_PATH" "$AUTH_PATH" "$BACKUP_DIR" <<'PY' +import shutil +import sys +from datetime import datetime +from pathlib import Path + +config_path = Path(sys.argv[1]) +auth_path = Path(sys.argv[2]) +backup_dir = Path(sys.argv[3]) + +def collect(path, kind, sets): + prefix = path.name + ".bak-" + for directory in (backup_dir, path.parent): + if not directory.exists(): + continue + for candidate in directory.glob(prefix + "*"): + if not candidate.is_file(): + continue + stamp = candidate.name[len(prefix):] + entry = sets.setdefault(stamp, {"timestamp": stamp, "config": None, "auth": None, "mtime": 0.0}) + candidate_mtime = candidate.stat().st_mtime + if entry[kind] is None or candidate_mtime > entry[kind].stat().st_mtime: + entry[kind] = candidate + entry["mtime"] = max(entry["mtime"], candidate_mtime) + +sets = {} +collect(config_path, "config", sets) +collect(auth_path, "auth", sets) +items = sorted(sets.values(), key=lambda item: item["mtime"], reverse=True) +if not items: + raise SystemExit("失败:未找到之前的 Codex config/auth 备份。") + +print("") +print("可用备份:") +for index, item in enumerate(items, 1): + labels = {"config": "配置", "auth": "认证"} + parts = "+".join(labels[kind] for kind in ("config", "auth") if item[kind] is not None) + file_time = datetime.fromtimestamp(item["mtime"]).strftime("%Y-%m-%d %H:%M:%S") + print(f"{index}) {item['timestamp']} [{parts}] 文件时间 {file_time}") + +choice = input("请选择备份序号:").strip() +try: + selected = items[int(choice) - 1] +except (ValueError, IndexError): + raise SystemExit("失败:备份序号无效。") + +if selected["config"] is not None: + shutil.copy2(selected["config"], config_path) + print(f"已恢复:{config_path}") +if selected["auth"] is not None: + shutil.copy2(selected["auth"], auth_path) + print(f"已恢复:{auth_path}") +print("已完成,之前的 Codex 配置已恢复。") +PY + stop_codex_processes + exit 0 + ;; + *) + echo "失败:参数无效:$PINAI_ACTION。直接运行会配置 ${siteName};恢复备份请使用 restore 参数。" >&2 + exit 1 + ;; +esac + +timestamp=$(new_backup_stamp) +if [ -f "$CONFIG_PATH" ]; then + config_backup="$BACKUP_DIR/$(basename "$CONFIG_PATH").bak-$timestamp" + cp "$CONFIG_PATH" "$config_backup" + echo "已备份:$config_backup" +fi +if [ -f "$AUTH_PATH" ]; then + auth_backup="$BACKUP_DIR/$(basename "$AUTH_PATH").bak-$timestamp" + cp "$AUTH_PATH" "$auth_backup" + echo "已备份:$auth_backup" +fi + +"$PYTHON_BIN" - "$CONFIG_PATH" "$AUTH_PATH" "$BASE_URL" "$API_KEY" "$ENABLE_WEBSOCKET" "$CONFIG_TOML_TEMPLATE" <<'PY' +import json +import sys +from pathlib import Path + +config_path = Path(sys.argv[1]) +auth_path = Path(sys.argv[2]) +base_url = sys.argv[3].rstrip("/") +api_key = sys.argv[4].strip() +enable_ws = sys.argv[5].lower() == "true" +template = sys.argv[6] + +if not api_key: + raise RuntimeError("缺少 API Key。") +if not base_url: + raise RuntimeError("缺少 Base URL。") + +if auth_path.exists() and auth_path.read_text(encoding="utf-8").strip(): + try: + json.loads(auth_path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + raise RuntimeError("auth.json 不是合法 JSON,请手动合并后重试。") from exc + +rendered_config = template.replace("{{PINAI_BASE_URL}}", base_url) +rendered_config = rendered_config.replace("{{PINAI_ENABLE_WEBSOCKET}}", "true" if enable_ws else "false") +rendered_config = rendered_config.replace("\\r\\n", "\\n").replace("\\r", "\\n").rstrip("\\n") + "\\n" +auth_text = json.dumps({"OPENAI_API_KEY": api_key}, ensure_ascii=False, separators=(",", ":")) + "\\n" + +config_path.write_text(rendered_config, encoding="utf-8") +auth_path.write_text(auth_text, encoding="utf-8") + +if config_path.read_text(encoding="utf-8") != rendered_config: + raise RuntimeError("config.toml 写入后回读不一致,请检查磁盘权限或安全软件拦截。") +actual_auth = json.loads(auth_path.read_text(encoding="utf-8")) +if actual_auth.get("OPENAI_API_KEY") != api_key: + raise RuntimeError("auth.json 写入后 API Key 校验失败。") +PY - const authJSON = json({ - OPENAI_API_KEY: input.apiKey +echo "回读校验:已确认配置和认证文件写入成功。" +echo "已完成,${siteName} Codex CLI 配置已更新。正在自动关闭 Codex..." +stop_codex_processes +` +} + +function buildCodexPowerShellPayload(input: Required>): string { + const siteName = input.siteName + const baseUrl = resolveBaseUrl(input.baseUrl) + const template = codexConfigTemplate(input) + + return `$ErrorActionPreference = "Stop" + +$BaseUrl = ${powershellSingleQuote(baseUrl)} +$ApiKey = ${powershellSingleQuote(input.apiKey)} +$EnableWebSocket = $false +$ConfigTomlTemplate = ${powershellSingleQuote(template)} +$SiteName = ${powershellSingleQuote(siteName)} + +function Stop-CodexProcesses { + if ($env:PINAI_SKIP_CODEX_PROCESS_CLOSE -eq "1") { + Write-Host "已跳过结束 Codex 进程。" + return + } + + $processes = @(Get-Process -ErrorAction SilentlyContinue | Where-Object { + $_.ProcessName -ieq "codex" -and $_.Id -ne $PID }) + if ($processes.Count -eq 0) { + Write-Host "未发现正在运行的 Codex 进程;重新打开 Codex 即可使用新配置。" + return + } - return { - label: 'Codex CLI', - files: [ - { path: '.codex/config.toml', content: configToml }, - { path: '.codex/auth.json', content: authJSON } - ] + $stopped = 0 + foreach ($process in $processes) { + Stop-Process -Id $process.Id -Force -ErrorAction Stop + $stopped += 1 } + Write-Host ("已结束 Codex 进程:{0} 个。" -f $stopped) } -function buildClaudePayload(input: Required> & Pick): ConfigPayload { - const baseUrl = input.platform === 'antigravity' +function New-BackupStamp { + param([string[]]$Paths, [string]$BackupDir) + $base = Get-Date -Format "yyyyMMdd-HHmmss" + $stamp = $base + $counter = 2 + while ($true) { + $exists = $false + foreach ($path in $Paths) { + $name = Split-Path -Leaf $path + if (Test-Path -LiteralPath (Join-Path $BackupDir "$name.bak-$stamp")) { + $exists = $true + break + } + } + if (-not $exists) { return $stamp } + $stamp = "{0}-{1:D2}" -f $base, $counter + $counter++ + } +} + +function Backup-File { + param([string]$Path, [string]$BackupDir, [string]$Timestamp) + if (Test-Path -LiteralPath $Path) { + New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null + $backup = Join-Path $BackupDir "$((Split-Path -Leaf $Path)).bak-$Timestamp" + Copy-Item -LiteralPath $Path -Destination $backup -Force + Write-Host "已备份:$backup" + } +} + +function Get-CodexBackupSets { + param([string]$ConfigPath, [string]$AuthPath, [string]$BackupDir) + $sets = @{} + foreach ($item in @(@{ Path = $ConfigPath; Kind = "Config" }, @{ Path = $AuthPath; Kind = "Auth" })) { + $name = Split-Path -Leaf $item.Path + foreach ($dir in @($BackupDir, (Split-Path -Parent $item.Path))) { + if (-not (Test-Path -LiteralPath $dir)) { continue } + foreach ($file in Get-ChildItem -LiteralPath $dir -File -Filter "$name.bak-*") { + $stamp = $file.Name.Substring(("$name.bak-").Length) + if (-not $sets.ContainsKey($stamp)) { + $sets[$stamp] = [ordered]@{ Timestamp = $stamp; Config = $null; Auth = $null; LastWriteTime = $file.LastWriteTime } + } + $entry = $sets[$stamp] + $entry[$item.Kind] = $file + if ($file.LastWriteTime -gt $entry.LastWriteTime) { $entry.LastWriteTime = $file.LastWriteTime } + } + } + } + return @($sets.Values | ForEach-Object { [pscustomobject]$_ } | Sort-Object LastWriteTime -Descending) +} + +function Restore-CodexBackup { + param([string]$ConfigPath, [string]$AuthPath, [string]$BackupDir) + $items = Get-CodexBackupSets $ConfigPath $AuthPath $BackupDir + if ($items.Count -eq 0) { throw "未找到之前的 Codex config/auth 备份。" } + Write-Host "" + Write-Host "可用备份:" + for ($i = 0; $i -lt $items.Count; $i++) { + $parts = @() + if ($null -ne $items[$i].Config) { $parts += "配置" } + if ($null -ne $items[$i].Auth) { $parts += "认证" } + Write-Host ("{0}) {1} [{2}] 文件时间 {3}" -f ($i + 1), $items[$i].Timestamp, ($parts -join "+"), $items[$i].LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")) + } + $choice = Read-Host "请选择备份序号" + $index = 0 + if (-not [int]::TryParse($choice.Trim(), [ref]$index) -or $index -lt 1 -or $index -gt $items.Count) { + throw "备份序号无效。" + } + $selected = $items[$index - 1] + if ($null -ne $selected.Config) { + Copy-Item -LiteralPath $selected.Config.FullName -Destination $ConfigPath -Force + Write-Host "已恢复:$ConfigPath" + } + if ($null -ne $selected.Auth) { + Copy-Item -LiteralPath $selected.Auth.FullName -Destination $AuthPath -Force + Write-Host "已恢复:$AuthPath" + } +} + +function Resolve-SetupAction { + param([string[]]$Arguments) + if ($null -eq $Arguments -or $Arguments.Count -eq 0) { return "apply" } + switch ($Arguments[0].Trim().ToLowerInvariant()) { + "" { return "apply" } + "1" { return "apply" } + "apply" { return "apply" } + "--apply" { return "apply" } + "2" { return "restore" } + "restore" { return "restore" } + "--restore" { return "restore" } + "/restore" { return "restore" } + default { throw "参数无效:$($Arguments[0])。直接运行会配置 $SiteName;恢复备份请使用 restore 参数。" } + } +} + +try { + if ([string]::IsNullOrWhiteSpace($env:USERPROFILE)) { throw "USERPROFILE 为空。" } + $codexDir = Join-Path $env:USERPROFILE ".codex" + $configPath = Join-Path $codexDir "config.toml" + $authPath = Join-Path $codexDir "auth.json" + $backupDir = Join-Path $codexDir "backups" + + if ((Resolve-SetupAction -Arguments $args) -eq "restore") { + Restore-CodexBackup $configPath $authPath $backupDir + Stop-CodexProcesses + exit 0 + } + + if ([string]::IsNullOrWhiteSpace($ApiKey)) { throw "缺少 API Key。" } + if ([string]::IsNullOrWhiteSpace($BaseUrl)) { throw "缺少 Base URL。" } + + New-Item -ItemType Directory -Force -Path $codexDir | Out-Null + New-Item -ItemType Directory -Force -Path $backupDir | Out-Null + if (Test-Path -LiteralPath $authPath) { + $rawAuth = [System.IO.File]::ReadAllText($authPath) + if (-not [string]::IsNullOrWhiteSpace($rawAuth)) { + $null = $rawAuth | ConvertFrom-Json -ErrorAction Stop + } + } + + $newConfig = $ConfigTomlTemplate.Replace("{{PINAI_BASE_URL}}", $BaseUrl.Trim().TrimEnd("/")) + $newConfig = $newConfig.Replace("{{PINAI_ENABLE_WEBSOCKET}}", $EnableWebSocket.ToString().ToLowerInvariant()) + $newConfig = $newConfig.TrimEnd([char[]]@([char]13, [char]10)) + [Environment]::NewLine + $newAuth = ([ordered]@{ OPENAI_API_KEY = $ApiKey.Trim() } | ConvertTo-Json -Compress) + [Environment]::NewLine + + $timestamp = New-BackupStamp -Paths @($configPath, $authPath) -BackupDir $backupDir + Backup-File $configPath $backupDir $timestamp + Backup-File $authPath $backupDir $timestamp + + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + [System.IO.File]::WriteAllText($configPath, $newConfig, $utf8NoBom) + [System.IO.File]::WriteAllText($authPath, $newAuth, $utf8NoBom) + + if ([System.IO.File]::ReadAllText($configPath) -ne $newConfig) { throw "config.toml 写入后回读不一致。" } + $actualAuth = [System.IO.File]::ReadAllText($authPath) | ConvertFrom-Json -ErrorAction Stop + if ($actualAuth.OPENAI_API_KEY -ne $ApiKey.Trim()) { throw "auth.json 写入后 API Key 校验失败。" } + + Write-Host "回读校验:已确认配置和认证文件写入成功。" + Write-Host "已完成,$SiteName Codex CLI 配置已更新。正在自动关闭 Codex..." + Stop-CodexProcesses + exit 0 +} catch { + Write-Host "失败:$($_.Exception.Message)" -ForegroundColor Red + exit 1 +} +` +} + +function buildCodexBatchScript(input: Required>): string { + const encoded = toBase64UTF8(buildCodexPowerShellPayload(input)) + return `@echo off +chcp 65001 >nul +setlocal +set "PINAI_CODEX_EXIT=1" +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$script = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); $path = Join-Path $env:TEMP 'pinai-codex-config.ps1'; Set-Content -Encoding UTF8 -Path $path -Value $script; & $path %*" +set "PINAI_CODEX_EXIT=%ERRORLEVEL%" +echo. +if "%PINAI_CODEX_EXIT%"=="0" ( + echo ============================================================ + echo [PINAI SETUP SUCCESS] ${input.siteName} Codex CLI config/restore completed. + echo Reopen Codex or the target client to load the new config. + echo ============================================================ +) else ( + echo ============================================================ + echo [PINAI SETUP FAILED] ${input.siteName} Codex CLI config/restore did not complete. Exit code: %PINAI_CODEX_EXIT% + echo Check the error above. If Codex is still open, close it manually and retry. + echo ============================================================ +) +pause +endlocal & exit /b %PINAI_CODEX_EXIT% +` +} + +function resolveClaudeBase(input: Required> & Pick): string { + return input.platform === 'antigravity' ? resolveAntigravityBase(input.baseUrl) : resolveBaseUrl(input.baseUrl) +} + +function buildClaudeShellScript(input: Required> & Pick): string { + const siteName = input.siteName + const baseUrl = resolveClaudeBase(input) + + return `#!/usr/bin/env sh +set -eu + +pinai_setup_result() { + rc=$? + if [ "$rc" -eq 0 ]; then + printf '\\n' + echo "============================================================" + echo "[PINAI SETUP SUCCESS] ${siteName} Claude Code 配置已完成。" + echo "请重新打开 Claude Code 或对应客户端,让新配置生效。" + echo "============================================================" + else + printf '\\n' >&2 + echo "============================================================" >&2 + echo "[PINAI SETUP FAILED] ${siteName} Claude Code 配置未完成或未完全生效。退出码:$rc" >&2 + echo "请查看上方失败原因;如 settings.json 无法自动合并,请手动处理后重试。" >&2 + echo "============================================================" >&2 + fi + trap - EXIT + exit "$rc" +} +trap pinai_setup_result EXIT + +BASE_URL=${shellQuote(baseUrl)} +API_KEY=${shellQuote(input.apiKey)} +CLAUDE_DIR="$HOME/.claude" +SETTINGS_PATH="$CLAUDE_DIR/settings.json" +BACKUP_DIR="$CLAUDE_DIR/backups" + +PYTHON_BIN= +if command -v python3 >/dev/null 2>&1 && python3 -c 'import sys' >/dev/null 2>&1; then + PYTHON_BIN=python3 +elif command -v python >/dev/null 2>&1 && python -c 'import sys' >/dev/null 2>&1; then + PYTHON_BIN=python +else + echo "Failed: a working python3 or python is required to merge existing Claude Code config safely." >&2 + exit 1 +fi + +mkdir -p "$CLAUDE_DIR" "$BACKUP_DIR" + +unique_backup_path() { + path=$1 + backup_dir=$2 + timestamp=$3 + name=$(basename "$path") + backup="$backup_dir/$name.bak-$timestamp" + counter=2 + while [ -e "$backup" ]; do + backup="$backup_dir/$name.bak-$timestamp-$(printf "%02d" "$counter")" + counter=$((counter + 1)) + done + printf '%s\\n' "$backup" +} + +timestamp=$(date +"%Y%m%d-%H%M%S") +if [ -f "$SETTINGS_PATH" ]; then + backup_path=$(unique_backup_path "$SETTINGS_PATH" "$BACKUP_DIR" "$timestamp") + cp "$SETTINGS_PATH" "$backup_path" + echo "Backup: $backup_path" +fi + +"$PYTHON_BIN" - "$SETTINGS_PATH" "$BASE_URL" "$API_KEY" <<'PY' +import json +import sys +from pathlib import Path + +settings_path = Path(sys.argv[1]) +base_url = sys.argv[2].rstrip("/") +api_key = sys.argv[3] + +if not api_key: + raise RuntimeError("Missing API key.") +if not base_url: + raise RuntimeError("Missing base URL.") + +settings = {} +if settings_path.exists() and settings_path.read_text(encoding="utf-8").strip(): + try: + settings = json.loads(settings_path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + raise RuntimeError("settings.json is not valid JSON. Please merge it manually.") from exc + if not isinstance(settings, dict): + raise RuntimeError("settings.json must contain a JSON object.") + +env = settings.get("env") +if not isinstance(env, dict): + env = {} +env.update({ + "ANTHROPIC_BASE_URL": base_url, + "ANTHROPIC_AUTH_TOKEN": api_key, + "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1", + "CLAUDE_CODE_ATTRIBUTION_HEADER": "0", +}) +settings["env"] = env + +settings_path.write_text(json.dumps(settings, indent=2, ensure_ascii=False) + "\\n", encoding="utf-8") +PY + +echo "Done. ${siteName} Claude Code config has been updated." +` +} + +function buildClaudePowerShellPayload(input: Required> & Pick): string { + const siteName = input.siteName + const baseUrl = resolveClaudeBase(input) + + return `$ErrorActionPreference = "Stop" + +$BaseUrl = ${powershellSingleQuote(baseUrl)} +$ApiKey = ${powershellSingleQuote(input.apiKey)} +$SiteName = ${powershellSingleQuote(siteName)} + +function ConvertTo-OrderedMap { + param([object]$Value) + $map = [ordered]@{} + if ($null -eq $Value) { return $map } + if ($Value -is [System.Collections.IDictionary]) { + foreach ($key in $Value.Keys) { $map[[string]$key] = $Value[$key] } + return $map + } + foreach ($prop in $Value.PSObject.Properties) { + $map[$prop.Name] = $prop.Value + } + return $map +} + +function Backup-File { + param( + [string]$Path, + [string]$BackupDir, + [string]$Timestamp + ) + if (Test-Path -LiteralPath $Path) { + New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null + $backup = Get-UniqueBackupPath $Path $BackupDir $Timestamp + Copy-Item -LiteralPath $Path -Destination $backup + Write-Host "Backup: $backup" + } +} + +function Get-UniqueBackupPath { + param( + [string]$Path, + [string]$BackupDir, + [string]$Timestamp + ) + $name = Split-Path -Leaf $Path + $backup = Join-Path $BackupDir "$name.bak-$Timestamp" + $counter = 2 + while (Test-Path -LiteralPath $backup) { + $backup = Join-Path $BackupDir ("{0}.bak-{1}-{2:D2}" -f $name, $Timestamp, $counter) + $counter++ + } + return $backup +} + +function Write-Utf8NoBom { + param( + [string]$Path, + [string]$Text + ) + $encoding = New-Object System.Text.UTF8Encoding($false) + [System.IO.File]::WriteAllText($Path, $Text, $encoding) +} + +try { + if ([string]::IsNullOrWhiteSpace($ApiKey)) { throw "Missing API key." } + if ([string]::IsNullOrWhiteSpace($BaseUrl)) { throw "Missing base URL." } + + $targetHome = $env:USERPROFILE + if ([string]::IsNullOrWhiteSpace($targetHome)) { throw "USERPROFILE is empty." } + + $claudeDir = Join-Path $targetHome ".claude" + $settingsPath = Join-Path $claudeDir "settings.json" + $backupDir = Join-Path $claudeDir "backups" + $settings = [ordered]@{} + + if (Test-Path -LiteralPath $settingsPath) { + $raw = [System.IO.File]::ReadAllText($settingsPath) + if (-not [string]::IsNullOrWhiteSpace($raw)) { + try { + $settings = ConvertTo-OrderedMap ($raw | ConvertFrom-Json) + } catch { + throw "settings.json is not valid JSON. Please merge it manually." + } + } + } + + $envMap = [ordered]@{} + if ($settings.Contains("env")) { + $envMap = ConvertTo-OrderedMap $settings["env"] + } + $envMap["ANTHROPIC_BASE_URL"] = $BaseUrl.Trim().TrimEnd("/") + $envMap["ANTHROPIC_AUTH_TOKEN"] = $ApiKey + $envMap["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1" + $envMap["CLAUDE_CODE_ATTRIBUTION_HEADER"] = "0" + $settings["env"] = $envMap + + Write-Host "Applying $SiteName Claude Code config..." + Write-Host "Settings: $settingsPath" + Write-Host "Base: $($BaseUrl.Trim().TrimEnd('/'))" + + New-Item -ItemType Directory -Force -Path $claudeDir | Out-Null + New-Item -ItemType Directory -Force -Path $backupDir | Out-Null + $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" + Backup-File $settingsPath $backupDir $timestamp + Write-Utf8NoBom $settingsPath (($settings | ConvertTo-Json -Depth 50) + [Environment]::NewLine) + + Write-Host "Done. $SiteName Claude Code config has been updated." + exit 0 +} catch { + Write-Host "Failed: $($_.Exception.Message)" -ForegroundColor Red + exit 1 +} +` +} + +function buildClaudeBatchScript(input: Required> & Pick): string { + const psScript = buildClaudePowerShellPayload(input) + return `@echo off +setlocal +set "PINAI_SETUP_SCRIPT_PATH=%~f0" +set "PINAI_SETUP_PAYLOAD=" +set "PINAI_SETUP_EXIT=1" +set "PINAI_SETUP_LAUNCHED_FROM_EXPLORER=0" +set "PINAI_SETUP_MARKER=__PINAI_CLAUDE_CODE_PS1__" +for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$p=[System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), '.ps1'); Write-Output $p"\`) do set "PINAI_SETUP_PAYLOAD=%%I" +for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$ErrorActionPreference='Stop'; try { $ps = Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $PID); $cmd = if ($null -ne $ps) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $ps.ParentProcessId) } else { $null }; $parent = if ($null -ne $cmd) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $cmd.ParentProcessId) } else { $null }; if ($null -ne $parent -and $parent.Name -ieq 'explorer.exe') { '1' } else { '0' } } catch { '0' }"\`) do set "PINAI_SETUP_LAUNCHED_FROM_EXPLORER=%%I" +if not defined PINAI_SETUP_PAYLOAD ( + set "PINAI_SETUP_EXIT=1" + goto :pinai_setup_done +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$p=$env:PINAI_SETUP_SCRIPT_PATH; $out=$env:PINAI_SETUP_PAYLOAD; $marker=$env:PINAI_SETUP_MARKER; $text=[System.IO.File]::ReadAllText($p); $idx=$text.LastIndexOf($marker); if($idx -lt 0){ Write-Error 'PowerShell payload marker not found.'; exit 1 }; $code=$text.Substring($idx + $marker.Length).TrimStart(); $enc=New-Object System.Text.UTF8Encoding($true); [System.IO.File]::WriteAllText($out, $code, $enc)" +if errorlevel 1 ( + set "PINAI_SETUP_EXIT=1" + goto :pinai_setup_done +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PINAI_SETUP_PAYLOAD%" +set "PINAI_SETUP_EXIT=%ERRORLEVEL%" +if exist "%PINAI_SETUP_PAYLOAD%" del /f /q "%PINAI_SETUP_PAYLOAD%" >nul 2>nul +:pinai_setup_done +echo. +if "%PINAI_SETUP_EXIT%"=="0" ( + echo ============================================================ + echo [PINAI SETUP SUCCESS] ${input.siteName} Claude Code config completed. + echo Reopen Claude Code or the target client to load the new config. + echo ============================================================ +) else ( + echo ============================================================ + echo [PINAI SETUP FAILED] ${input.siteName} Claude Code config did not complete. Exit code: %PINAI_SETUP_EXIT% + echo Check the error above. If settings.json cannot be merged automatically, handle it manually and retry. + echo ============================================================ +) +if "%PINAI_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( + echo. + echo 按任意键关闭窗口... + pause >nul +) +endlocal & exit /b %PINAI_SETUP_EXIT% + +__PINAI_CLAUDE_CODE_PS1__ +${psScript}` +} + +function buildClaudePayload(input: Required> & Pick): ConfigPayload { + const baseUrl = resolveClaudeBase(input) const env = { ANTHROPIC_BASE_URL: baseUrl, ANTHROPIC_AUTH_TOKEN: input.apiKey, @@ -162,7 +885,7 @@ function buildPayload(input: ConfigScriptInput): ConfigPayload { switch (input.client) { case 'codex': - return buildCodexPayload(base) + throw new Error('Codex CLI uses a dedicated setup script generator.') case 'claude': return buildClaudePayload({ ...base, platform: input.platform }) case 'opencode': @@ -264,10 +987,19 @@ export function getConfigScriptOS(): ConfigScriptOS { export function buildAPIKeyConfigScript(input: ConfigScriptInput): { filename: string; content: string; os: ConfigScriptOS } { const os = input.os || getConfigScriptOS() const siteName = input.siteName || CONFIG_SCRIPT_SITE_NAME - const payload = buildPayload({ ...input, siteName }) const filenameClient = input.client === 'claude' ? 'claude-code' : input.client const extension = os === 'win' ? 'bat' : 'sh' - const content = os === 'win' ? buildBatchScript(payload, siteName) : buildShellScript(payload, siteName) + const content = input.client === 'codex' + ? os === 'win' + ? buildCodexBatchScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName }) + : buildCodexShellScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName }) + : input.client === 'claude' + ? os === 'win' + ? buildClaudeBatchScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName, platform: input.platform }) + : buildClaudeShellScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName, platform: input.platform }) + : os === 'win' + ? buildBatchScript(buildPayload({ ...input, siteName }), siteName) + : buildShellScript(buildPayload({ ...input, siteName }), siteName) return { filename: `${siteName}-${filenameClient}-config.${extension}`, @@ -277,14 +1009,7 @@ export function buildAPIKeyConfigScript(input: ConfigScriptInput): { filename: s } export function isConfigScriptClientAvailable(input: Pick): boolean { - switch (input.client) { - case 'codex': - return input.platform === 'openai' - case 'claude': - return input.platform === 'anthropic' || input.platform === 'antigravity' || (input.platform === 'openai' && input.allowMessagesDispatch === true) - case 'opencode': - return !!input.platform - } + return !!input.platform } export function downloadAPIKeyConfigScript(input: ConfigScriptInput): void { diff --git a/frontend/src/views/user/KeysView.vue b/frontend/src/views/user/KeysView.vue index 2b68cc42cb3..31d4b053294 100644 --- a/frontend/src/views/user/KeysView.vue +++ b/frontend/src/views/user/KeysView.vue @@ -1600,7 +1600,8 @@ const downloadConfigScript = (client: ConfigScriptClient) => { platform: key.group.platform, allowMessagesDispatch: key.group.allow_messages_dispatch || false, baseUrl: publicSettings.value?.api_base_url || window.location.origin, - apiKey: key.key + apiKey: key.key, + siteName: publicSettings.value?.site_name || 'Sub2API' }) appStore.showSuccess(t('keys.configScriptMenu.downloadStarted')) closeConfigScriptMenu() From 4da51c4a174d950158ad6ce5f2f4b2684d22c676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 15:49:45 +0800 Subject: [PATCH 04/20] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/configScriptDownload.spec.ts | 87 ++- frontend/src/utils/configScriptDownload.ts | 634 ++++++++++++++++-- frontend/src/views/user/KeysView.vue | 2 +- 3 files changed, 659 insertions(+), 64 deletions(-) diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index 66c987b0e80..2d11e2f7b55 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -13,12 +13,19 @@ function decodeBatchPayload(content: string): string { } function extractClaudeBatchPayload(content: string): string { - const marker = '__PINAI_CLAUDE_CODE_PS1__' + const marker = '__LOOK2EYE_CLAUDE_CODE_PS1__' const markerIndex = content.lastIndexOf(marker) if (markerIndex < 0) throw new Error('missing Claude Code payload marker') return content.slice(markerIndex + marker.length).trimStart() } +function extractOpenCodeBatchPayload(content: string): string { + const marker = '__LOOK2EYE_OPENCODE_PS1__' + const markerIndex = content.lastIndexOf(marker) + if (markerIndex < 0) throw new Error('missing OpenCode payload marker') + return content.slice(markerIndex + marker.length).trimStart() +} + describe('configScriptDownload', () => { it('builds a macOS Codex shell script with look2eye config values', () => { const script = buildAPIKeyConfigScript({ @@ -38,7 +45,7 @@ describe('configScriptDownload', () => { expect(script.content).toContain('AUTH_PATH="$CODEX_DIR/auth.json"') expect(script.content).toContain('BACKUP_DIR="$CODEX_DIR/backups"') expect(script.content).toContain('restore|--restore|/restore') - expect(script.content).toContain('base_url = "{{PINAI_BASE_URL}}"') + expect(script.content).toContain('base_url = "{{LOOK2EYE_BASE_URL}}"') expect(script.content).toContain('json.dumps({"OPENAI_API_KEY": api_key}') expect(script.content).toContain('stop_codex_processes') }) @@ -50,18 +57,18 @@ describe('configScriptDownload', () => { platform: 'openai', baseUrl: 'https://api.example.com/v1', apiKey: 'sk-win-key', - siteName: 'PinAI' + siteName: 'Look2eye' }) - expect(script.filename).toBe('PinAI-codex-config.bat') + expect(script.filename).toBe('Look2eye-codex-config.bat') expect(script.content).toContain('@echo off') - expect(script.content).toContain('PINAI_CODEX_EXIT') + expect(script.content).toContain('LOOK2EYE_CODEX_EXIT') const payload = decodeBatchPayload(script.content) expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") expect(payload).toContain("$ApiKey = 'sk-win-key'") expect(payload).toContain('$ConfigTomlTemplate') - expect(payload).toContain('model_provider = "PinAI"') + expect(payload).toContain('model_provider = "Look2eye"') expect(payload).toContain('Restore-CodexBackup') expect(payload).toContain('Stop-CodexProcesses') }) @@ -73,12 +80,12 @@ describe('configScriptDownload', () => { platform: 'anthropic', baseUrl: 'https://api.example.com/v1/', apiKey: 'sk-claude-key', - siteName: 'PinAI' + siteName: 'Look2eye' }) - expect(script.filename).toBe('PinAI-claude-code-config.sh') + expect(script.filename).toBe('Look2eye-claude-code-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') - expect(script.content).toContain('PinAI Claude Code 配置已完成') + expect(script.content).toContain('Look2eye Claude Code 配置已完成') expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") expect(script.content).toContain("API_KEY='sk-claude-key'") expect(script.content).toContain('CLAUDE_DIR="$HOME/.claude"') @@ -103,9 +110,9 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-claude-code-config.bat') expect(script.content).toContain('@echo off') - expect(script.content).toContain('PINAI_SETUP_MARKER=__PINAI_CLAUDE_CODE_PS1__') - expect(script.content).toContain('__PINAI_CLAUDE_CODE_PS1__') - expect(script.content).toContain('PINAI_SETUP_LAUNCHED_FROM_EXPLORER') + expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CLAUDE_CODE_PS1__') + expect(script.content).toContain('__LOOK2EYE_CLAUDE_CODE_PS1__') + expect(script.content).toContain('LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER') const payload = extractClaudeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName Claude Code config") @@ -120,6 +127,62 @@ describe('configScriptDownload', () => { expect(payload).toContain('Write-Utf8NoBom') }) + it('builds a macOS OpenCode shell script that merges opencode.json', () => { + const script = buildAPIKeyConfigScript({ + client: 'opencode', + os: 'mac', + platform: 'openai', + baseUrl: 'https://api.example.com/v1/', + apiKey: 'sk-opencode-key', + siteName: 'Look2eye' + }) + + expect(script.filename).toBe('Look2eye-opencode-config.sh') + expect(script.content).toContain('#!/usr/bin/env sh') + expect(script.content).toContain('Look2eye OpenCode 配置已完成') + expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") + expect(script.content).toContain("API_KEY='sk-opencode-key'") + expect(script.content).toContain('OPENCODE_DIR="$HOME/.config/opencode"') + expect(script.content).toContain('CONFIG_PATH="$OPENCODE_DIR/opencode.json"') + expect(script.content).toContain('BACKUP_DIR="$OPENCODE_DIR/backups"') + expect(script.content).toContain('default_models = json.loads') + expect(script.content).toContain('"gpt-5.5"') + expect(script.content).toContain('options["baseURL"] = base_url') + expect(script.content).toContain('options["apiKey"] = api_key') + expect(script.content).toContain('opts["store"] = False') + expect(script.content).not.toContain('Installing Look2eye OpenCode configuration') + }) + + it('builds a Windows OpenCode batch script from the marker-based template', () => { + const script = buildAPIKeyConfigScript({ + client: 'opencode', + os: 'win', + platform: 'gemini', + baseUrl: 'https://api.example.com/v1', + apiKey: 'sk-opencode-win-key' + }) + + expect(script.filename).toBe('look2eye-opencode-config.bat') + expect(script.content).toContain('@echo off') + expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_OPENCODE_PS1__') + expect(script.content).toContain('__LOOK2EYE_OPENCODE_PS1__') + expect(script.content).toContain('LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER') + + const payload = extractOpenCodeBatchPayload(script.content) + expect(payload).toContain("Applying $SiteName OpenCode config") + expect(payload).toContain("Join-Path (Join-Path $targetHome \".config\") \"opencode\"") + expect(payload).toContain('Join-Path $openCodeDir "opencode.json"') + expect(payload).toContain('Join-Path $openCodeDir "backups"') + expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") + expect(payload).toContain("$ApiKey = 'sk-opencode-win-key'") + expect(payload).toContain('$ModelsJson') + expect(payload).toContain('"gpt-5.5"') + expect(payload).toContain('$options["baseURL"] = $BaseUrl.Trim().TrimEnd("/")') + expect(payload).toContain('$options["apiKey"] = $ApiKey') + expect(payload).toContain('Ensure-AgentStoreFalse') + expect(payload).toContain('Write-Utf8NoBom') + }) + it('allows config scripts for every grouped platform', () => { expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'openai' })).toBe(true) expect(isConfigScriptClientAvailable({ client: 'codex', platform: 'gemini' })).toBe(true) diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index 116e9828614..6c940df8d3d 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -31,6 +31,176 @@ const CODEX_DEFAULT_REVIEW_MODEL = 'gpt-5.5' const CODEX_REASONING_EFFORT = 'xhigh' const CODEX_CONTEXT_WINDOW = 1000000 const CODEX_AUTO_COMPACT_TOKEN_LIMIT = 900000 +const OPENCODE_DEFAULT_MODELS_JSON = `{ + "gpt-5.2": { + "name": "GPT-5.2", + "limit": { + "context": 400000, + "output": 128000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + }, + "gpt-5.5": { + "name": "GPT-5.5", + "limit": { + "context": 1050000, + "output": 128000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + }, + "gpt-5.4": { + "name": "GPT-5.4", + "limit": { + "context": 1050000, + "output": 128000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + }, + "gpt-5.4-mini": { + "name": "GPT-5.4 Mini", + "limit": { + "context": 400000, + "output": 128000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + }, + "gpt-5.3-codex-spark": { + "name": "GPT-5.3 Codex Spark", + "limit": { + "context": 128000, + "output": 32000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + }, + "gpt-5.3-codex": { + "name": "GPT-5.3 Codex", + "limit": { + "context": 400000, + "output": 128000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + }, + "codex-mini-latest": { + "name": "Codex Mini", + "limit": { + "context": 200000, + "output": 100000 + }, + "options": { + "store": false + }, + "variants": { + "low": { + "reasoningEffort": "low" + }, + "medium": { + "reasoningEffort": "medium" + }, + "high": { + "reasoningEffort": "high" + }, + "xhigh": { + "reasoningEffort": "xhigh" + } + } + } +}` const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '') @@ -64,7 +234,7 @@ function json(value: unknown): string { function codexProviderName(siteName: string): string { const sanitized = siteName.replace(/[^A-Za-z0-9_-]+/g, '') - return sanitized || 'PinAI' + return sanitized || 'Look2eye' } function codexConfigTemplate(input: Required>): string { @@ -85,12 +255,12 @@ max_depth = 1 [model_providers.${providerName}] name = "${providerName}" -base_url = "{{PINAI_BASE_URL}}" +base_url = "{{LOOK2EYE_BASE_URL}}" wire_api = "responses" -supports_websockets = {{PINAI_ENABLE_WEBSOCKET}} +supports_websockets = {{LOOK2EYE_ENABLE_WEBSOCKET}} [features] -responses_websockets_v2 = {{PINAI_ENABLE_WEBSOCKET}} +responses_websockets_v2 = {{LOOK2EYE_ENABLE_WEBSOCKET}} goals = true [windows] @@ -106,25 +276,25 @@ function buildCodexShellScript(input: Required&2 echo "============================================================" >&2 - echo "[PINAI SETUP FAILED] ${siteName} Codex CLI 配置未完成或未完全生效。退出码:$rc" >&2 + echo "[LOOK2EYE SETUP FAILED] ${siteName} Codex CLI 配置未完成或未完全生效。退出码:$rc" >&2 echo "请查看上方失败原因;如提示 Codex 未关闭,请手动关闭后重试。" >&2 echo "============================================================" >&2 fi trap - EXIT exit "$rc" } -trap pinai_setup_result EXIT +trap look2eye_setup_result EXIT BASE_URL=${shellQuote(baseUrl)} API_KEY=${shellQuote(input.apiKey)} @@ -138,7 +308,7 @@ BACKUP_DIR="$CODEX_DIR/backups" mkdir -p "$CODEX_DIR" "$BACKUP_DIR" stop_codex_processes() { - if [ "\${PINAI_SKIP_CODEX_PROCESS_CLOSE:-}" = "1" ]; then + if [ "\${LOOK2EYE_SKIP_CODEX_PROCESS_CLOSE:-}" = "1" ]; then echo "已跳过结束 Codex 进程。" return 0 fi @@ -199,9 +369,9 @@ else exit 1 fi -PINAI_ACTION=\${1:-apply} +LOOK2EYE_ACTION=\${1:-apply} -case "$PINAI_ACTION" in +case "$LOOK2EYE_ACTION" in ""|1|apply|--apply) printf "\\n${siteName} Codex CLI 一键配置:默认覆盖当前配置。\\n" echo "如需恢复备份,请在终端运行本脚本并追加 restore 参数。" @@ -265,7 +435,7 @@ PY exit 0 ;; *) - echo "失败:参数无效:$PINAI_ACTION。直接运行会配置 ${siteName};恢复备份请使用 restore 参数。" >&2 + echo "失败:参数无效:$LOOK2EYE_ACTION。直接运行会配置 ${siteName};恢复备份请使用 restore 参数。" >&2 exit 1 ;; esac @@ -305,8 +475,8 @@ if auth_path.exists() and auth_path.read_text(encoding="utf-8").strip(): except json.JSONDecodeError as exc: raise RuntimeError("auth.json 不是合法 JSON,请手动合并后重试。") from exc -rendered_config = template.replace("{{PINAI_BASE_URL}}", base_url) -rendered_config = rendered_config.replace("{{PINAI_ENABLE_WEBSOCKET}}", "true" if enable_ws else "false") +rendered_config = template.replace("{{LOOK2EYE_BASE_URL}}", base_url) +rendered_config = rendered_config.replace("{{LOOK2EYE_ENABLE_WEBSOCKET}}", "true" if enable_ws else "false") rendered_config = rendered_config.replace("\\r\\n", "\\n").replace("\\r", "\\n").rstrip("\\n") + "\\n" auth_text = json.dumps({"OPENAI_API_KEY": api_key}, ensure_ascii=False, separators=(",", ":")) + "\\n" @@ -340,7 +510,7 @@ $ConfigTomlTemplate = ${powershellSingleQuote(template)} $SiteName = ${powershellSingleQuote(siteName)} function Stop-CodexProcesses { - if ($env:PINAI_SKIP_CODEX_PROCESS_CLOSE -eq "1") { + if ($env:LOOK2EYE_SKIP_CODEX_PROCESS_CLOSE -eq "1") { Write-Host "已跳过结束 Codex 进程。" return } @@ -481,8 +651,8 @@ try { } } - $newConfig = $ConfigTomlTemplate.Replace("{{PINAI_BASE_URL}}", $BaseUrl.Trim().TrimEnd("/")) - $newConfig = $newConfig.Replace("{{PINAI_ENABLE_WEBSOCKET}}", $EnableWebSocket.ToString().ToLowerInvariant()) + $newConfig = $ConfigTomlTemplate.Replace("{{LOOK2EYE_BASE_URL}}", $BaseUrl.Trim().TrimEnd("/")) + $newConfig = $newConfig.Replace("{{LOOK2EYE_ENABLE_WEBSOCKET}}", $EnableWebSocket.ToString().ToLowerInvariant()) $newConfig = $newConfig.TrimEnd([char[]]@([char]13, [char]10)) + [Environment]::NewLine $newAuth = ([ordered]@{ OPENAI_API_KEY = $ApiKey.Trim() } | ConvertTo-Json -Compress) + [Environment]::NewLine @@ -514,23 +684,23 @@ function buildCodexBatchScript(input: Requirednul setlocal -set "PINAI_CODEX_EXIT=1" -powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$script = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); $path = Join-Path $env:TEMP 'pinai-codex-config.ps1'; Set-Content -Encoding UTF8 -Path $path -Value $script; & $path %*" -set "PINAI_CODEX_EXIT=%ERRORLEVEL%" +set "LOOK2EYE_CODEX_EXIT=1" +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$script = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); $path = Join-Path $env:TEMP 'look2eye-codex-config.ps1'; Set-Content -Encoding UTF8 -Path $path -Value $script; & $path %*" +set "LOOK2EYE_CODEX_EXIT=%ERRORLEVEL%" echo. -if "%PINAI_CODEX_EXIT%"=="0" ( +if "%LOOK2EYE_CODEX_EXIT%"=="0" ( echo ============================================================ - echo [PINAI SETUP SUCCESS] ${input.siteName} Codex CLI config/restore completed. + echo [LOOK2EYE SETUP SUCCESS] ${input.siteName} Codex CLI config/restore completed. echo Reopen Codex or the target client to load the new config. echo ============================================================ ) else ( echo ============================================================ - echo [PINAI SETUP FAILED] ${input.siteName} Codex CLI config/restore did not complete. Exit code: %PINAI_CODEX_EXIT% + echo [LOOK2EYE SETUP FAILED] ${input.siteName} Codex CLI config/restore did not complete. Exit code: %LOOK2EYE_CODEX_EXIT% echo Check the error above. If Codex is still open, close it manually and retry. echo ============================================================ ) pause -endlocal & exit /b %PINAI_CODEX_EXIT% +endlocal & exit /b %LOOK2EYE_CODEX_EXIT% ` } @@ -547,25 +717,25 @@ function buildClaudeShellScript(input: Required&2 echo "============================================================" >&2 - echo "[PINAI SETUP FAILED] ${siteName} Claude Code 配置未完成或未完全生效。退出码:$rc" >&2 + echo "[LOOK2EYE SETUP FAILED] ${siteName} Claude Code 配置未完成或未完全生效。退出码:$rc" >&2 echo "请查看上方失败原因;如 settings.json 无法自动合并,请手动处理后重试。" >&2 echo "============================================================" >&2 fi trap - EXIT exit "$rc" } -trap pinai_setup_result EXIT +trap look2eye_setup_result EXIT BASE_URL=${shellQuote(baseUrl)} API_KEY=${shellQuote(input.apiKey)} @@ -766,46 +936,404 @@ function buildClaudeBatchScript(input: Requirednul 2>nul -:pinai_setup_done +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%" +set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" +if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul +:look2eye_setup_done echo. -if "%PINAI_SETUP_EXIT%"=="0" ( +if "%LOOK2EYE_SETUP_EXIT%"=="0" ( echo ============================================================ - echo [PINAI SETUP SUCCESS] ${input.siteName} Claude Code config completed. + echo [LOOK2EYE SETUP SUCCESS] ${input.siteName} Claude Code config completed. echo Reopen Claude Code or the target client to load the new config. echo ============================================================ ) else ( echo ============================================================ - echo [PINAI SETUP FAILED] ${input.siteName} Claude Code config did not complete. Exit code: %PINAI_SETUP_EXIT% + echo [LOOK2EYE SETUP FAILED] ${input.siteName} Claude Code config did not complete. Exit code: %LOOK2EYE_SETUP_EXIT% echo Check the error above. If settings.json cannot be merged automatically, handle it manually and retry. echo ============================================================ ) -if "%PINAI_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( +if "%LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( + echo. + echo 按任意键关闭窗口... + pause >nul +) +endlocal & exit /b %LOOK2EYE_SETUP_EXIT% + +__LOOK2EYE_CLAUDE_CODE_PS1__ +${psScript}` +} + +function buildOpenCodeShellScript(input: Required>): string { + const siteName = input.siteName + const baseUrl = resolveBaseUrl(input.baseUrl) + + return `#!/usr/bin/env sh +set -eu + +look2eye_setup_result() { + rc=$? + if [ "$rc" -eq 0 ]; then + printf '\\n' + echo "============================================================" + echo "[LOOK2EYE SETUP SUCCESS] ${siteName} OpenCode 配置已完成。" + echo "请重新打开 OpenCode 或对应客户端,让新配置生效。" + echo "============================================================" + else + printf '\\n' >&2 + echo "============================================================" >&2 + echo "[LOOK2EYE SETUP FAILED] ${siteName} OpenCode 配置未完成或未完全生效。退出码:$rc" >&2 + echo "请查看上方失败原因;如 opencode.json 无法自动合并,请手动处理后重试。" >&2 + echo "============================================================" >&2 + fi + trap - EXIT + exit "$rc" +} +trap look2eye_setup_result EXIT + +BASE_URL=${shellQuote(baseUrl)} +API_KEY=${shellQuote(input.apiKey)} +OPENCODE_DIR="$HOME/.config/opencode" +CONFIG_PATH="$OPENCODE_DIR/opencode.json" +BACKUP_DIR="$OPENCODE_DIR/backups" + +PYTHON_BIN= +if command -v python3 >/dev/null 2>&1 && python3 -c 'import sys' >/dev/null 2>&1; then + PYTHON_BIN=python3 +elif command -v python >/dev/null 2>&1 && python -c 'import sys' >/dev/null 2>&1; then + PYTHON_BIN=python +else + echo "Failed: a working python3 or python is required to merge existing OpenCode config safely." >&2 + exit 1 +fi + +mkdir -p "$OPENCODE_DIR" "$BACKUP_DIR" + +unique_backup_path() { + path=$1 + backup_dir=$2 + timestamp=$3 + name=$(basename "$path") + backup="$backup_dir/$name.bak-$timestamp" + counter=2 + while [ -e "$backup" ]; do + backup="$backup_dir/$name.bak-$timestamp-$(printf "%02d" "$counter")" + counter=$((counter + 1)) + done + printf '%s\\n' "$backup" +} + +timestamp=$(date +"%Y%m%d-%H%M%S") +if [ -f "$CONFIG_PATH" ]; then + backup_path=$(unique_backup_path "$CONFIG_PATH" "$BACKUP_DIR" "$timestamp") + cp "$CONFIG_PATH" "$backup_path" + echo "Backup: $backup_path" +fi + +"$PYTHON_BIN" - "$CONFIG_PATH" "$BASE_URL" "$API_KEY" <<'PY' +import json +import sys +from pathlib import Path + +config_path = Path(sys.argv[1]) +base_url = sys.argv[2].rstrip("/") +api_key = sys.argv[3] +default_models = json.loads(r'''${OPENCODE_DEFAULT_MODELS_JSON}''') + +if not api_key: + raise RuntimeError("Missing API key.") +if not base_url: + raise RuntimeError("Missing base URL.") + +config = {} +if config_path.exists() and config_path.read_text(encoding="utf-8").strip(): + try: + config = json.loads(config_path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + raise RuntimeError("opencode.json is not valid JSON. Please merge it manually.") from exc + if not isinstance(config, dict): + raise RuntimeError("opencode.json must contain a JSON object.") + +provider = config.get("provider") +if not isinstance(provider, dict): + provider = {} +openai = provider.get("openai") +if not isinstance(openai, dict): + openai = {} +options = openai.get("options") +if not isinstance(options, dict): + options = {} +options["baseURL"] = base_url +options["apiKey"] = api_key +openai["options"] = options + +models = openai.get("models") +if not isinstance(models, dict): + models = {} +for model_name, model_config in default_models.items(): + existing_model = models.get(model_name) + if isinstance(existing_model, dict): + existing_model["variants"] = model_config.get("variants", {}) + models[model_name] = existing_model + else: + models[model_name] = model_config +openai["models"] = models +provider["openai"] = openai +config["provider"] = provider +config.setdefault("$schema", "https://opencode.ai/config.json") + +agent = config.get("agent") +if not isinstance(agent, dict): + agent = {} +for section_name in ("build", "plan"): + section = agent.get(section_name) + if not isinstance(section, dict): + section = {} + opts = section.get("options") + if not isinstance(opts, dict): + opts = {} + opts["store"] = False + section["options"] = opts + agent[section_name] = section +config["agent"] = agent + +config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\\n", encoding="utf-8") +PY + +echo "Done. ${siteName} OpenCode config has been updated." +` +} + +function buildOpenCodePowerShellPayload(input: Required>): string { + const siteName = input.siteName + const baseUrl = resolveBaseUrl(input.baseUrl) + + return `$ErrorActionPreference = "Stop" + +$BaseUrl = ${powershellSingleQuote(baseUrl)} +$ApiKey = ${powershellSingleQuote(input.apiKey)} +$SiteName = ${powershellSingleQuote(siteName)} +$ModelsJson = @' +${OPENCODE_DEFAULT_MODELS_JSON} +'@ + +function ConvertTo-OrderedMap { + param([object]$Value) + $map = [ordered]@{} + if ($null -eq $Value) { return $map } + if ($Value -is [System.Collections.IDictionary]) { + foreach ($key in $Value.Keys) { $map[[string]$key] = $Value[$key] } + return $map + } + foreach ($prop in $Value.PSObject.Properties) { + $map[$prop.Name] = $prop.Value + } + return $map +} + +function Backup-File { + param( + [string]$Path, + [string]$BackupDir, + [string]$Timestamp + ) + if (Test-Path -LiteralPath $Path) { + New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null + $backup = Get-UniqueBackupPath $Path $BackupDir $Timestamp + Copy-Item -LiteralPath $Path -Destination $backup + Write-Host "Backup: $backup" + } +} + +function Get-UniqueBackupPath { + param( + [string]$Path, + [string]$BackupDir, + [string]$Timestamp + ) + $name = Split-Path -Leaf $Path + $backup = Join-Path $BackupDir "$name.bak-$Timestamp" + $counter = 2 + while (Test-Path -LiteralPath $backup) { + $backup = Join-Path $BackupDir ("{0}.bak-{1}-{2:D2}" -f $name, $Timestamp, $counter) + $counter++ + } + return $backup +} + +function Write-Utf8NoBom { + param( + [string]$Path, + [string]$Text + ) + $encoding = New-Object System.Text.UTF8Encoding($false) + [System.IO.File]::WriteAllText($Path, $Text, $encoding) +} + +function Ensure-AgentStoreFalse { + param([object]$Config) + $agent = [ordered]@{} + if ($Config.Contains("agent")) { + $agent = ConvertTo-OrderedMap $Config["agent"] + } + foreach ($sectionName in @("build", "plan")) { + $section = [ordered]@{} + if ($agent.Contains($sectionName)) { + $section = ConvertTo-OrderedMap $agent[$sectionName] + } + $options = [ordered]@{} + if ($section.Contains("options")) { + $options = ConvertTo-OrderedMap $section["options"] + } + $options["store"] = $false + $section["options"] = $options + $agent[$sectionName] = $section + } + $Config["agent"] = $agent +} + +try { + if ([string]::IsNullOrWhiteSpace($ApiKey)) { throw "Missing API key." } + if ([string]::IsNullOrWhiteSpace($BaseUrl)) { throw "Missing base URL." } + + $targetHome = $env:USERPROFILE + if ([string]::IsNullOrWhiteSpace($targetHome)) { throw "USERPROFILE is empty." } + + $openCodeDir = Join-Path (Join-Path $targetHome ".config") "opencode" + $configPath = Join-Path $openCodeDir "opencode.json" + $backupDir = Join-Path $openCodeDir "backups" + $config = [ordered]@{} + + if (Test-Path -LiteralPath $configPath) { + $raw = [System.IO.File]::ReadAllText($configPath) + if (-not [string]::IsNullOrWhiteSpace($raw)) { + try { + $config = ConvertTo-OrderedMap ($raw | ConvertFrom-Json) + } catch { + throw "opencode.json is not valid JSON. Please merge it manually." + } + } + } + + $provider = [ordered]@{} + if ($config.Contains("provider")) { + $provider = ConvertTo-OrderedMap $config["provider"] + } + $openai = [ordered]@{} + if ($provider.Contains("openai")) { + $openai = ConvertTo-OrderedMap $provider["openai"] + } + $options = [ordered]@{} + if ($openai.Contains("options")) { + $options = ConvertTo-OrderedMap $openai["options"] + } + $options["baseURL"] = $BaseUrl.Trim().TrimEnd("/") + $options["apiKey"] = $ApiKey + $openai["options"] = $options + + $models = [ordered]@{} + if ($openai.Contains("models")) { + $models = ConvertTo-OrderedMap $openai["models"] + } + $defaultModels = ConvertTo-OrderedMap ($ModelsJson | ConvertFrom-Json) + foreach ($modelName in $defaultModels.Keys) { + if (-not $models.Contains($modelName)) { + $models[$modelName] = $defaultModels[$modelName] + } else { + $model = ConvertTo-OrderedMap $models[$modelName] + $defaultModel = ConvertTo-OrderedMap $defaultModels[$modelName] + if ($defaultModel.Contains("variants")) { + $model["variants"] = ConvertTo-OrderedMap $defaultModel["variants"] + } + $models[$modelName] = $model + } + } + $openai["models"] = $models + $provider["openai"] = $openai + $config["provider"] = $provider + if (-not $config.Contains('$schema')) { + $config['$schema'] = "https://opencode.ai/config.json" + } + Ensure-AgentStoreFalse $config + + Write-Host "Applying $SiteName OpenCode config..." + Write-Host "Config: $configPath" + Write-Host "Base: $($BaseUrl.Trim().TrimEnd('/'))" + + New-Item -ItemType Directory -Force -Path $openCodeDir | Out-Null + New-Item -ItemType Directory -Force -Path $backupDir | Out-Null + $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" + Backup-File $configPath $backupDir $timestamp + Write-Utf8NoBom $configPath (($config | ConvertTo-Json -Depth 80) + [Environment]::NewLine) + + Write-Host "Done. $SiteName OpenCode config has been updated." + exit 0 +} catch { + Write-Host "Failed: $($_.Exception.Message)" -ForegroundColor Red + exit 1 +} +` +} + +function buildOpenCodeBatchScript(input: Required>): string { + const psScript = buildOpenCodePowerShellPayload(input) + return `@echo off +setlocal +set "LOOK2EYE_SETUP_SCRIPT_PATH=%~f0" +set "LOOK2EYE_SETUP_PAYLOAD=" +set "LOOK2EYE_SETUP_EXIT=1" +set "LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER=0" +set "LOOK2EYE_SETUP_MARKER=__LOOK2EYE_OPENCODE_PS1__" +for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$p=[System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), '.ps1'); Write-Output $p"\`) do set "LOOK2EYE_SETUP_PAYLOAD=%%I" +for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$ErrorActionPreference='Stop'; try { $ps = Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $PID); $cmd = if ($null -ne $ps) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $ps.ParentProcessId) } else { $null }; $parent = if ($null -ne $cmd) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $cmd.ParentProcessId) } else { $null }; if ($null -ne $parent -and $parent.Name -ieq 'explorer.exe') { '1' } else { '0' } } catch { '0' }"\`) do set "LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER=%%I" +if not defined LOOK2EYE_SETUP_PAYLOAD ( + set "LOOK2EYE_SETUP_EXIT=1" + goto :look2eye_setup_done +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$p=$env:LOOK2EYE_SETUP_SCRIPT_PATH; $out=$env:LOOK2EYE_SETUP_PAYLOAD; $marker=$env:LOOK2EYE_SETUP_MARKER; $text=[System.IO.File]::ReadAllText($p); $idx=$text.LastIndexOf($marker); if($idx -lt 0){ Write-Error 'PowerShell payload marker not found.'; exit 1 }; $code=$text.Substring($idx + $marker.Length).TrimStart(); $enc=New-Object System.Text.UTF8Encoding($true); [System.IO.File]::WriteAllText($out, $code, $enc)" +if errorlevel 1 ( + set "LOOK2EYE_SETUP_EXIT=1" + goto :look2eye_setup_done +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%" +set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" +if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul +:look2eye_setup_done +echo. +if "%LOOK2EYE_SETUP_EXIT%"=="0" ( + echo ============================================================ + echo [LOOK2EYE SETUP SUCCESS] ${input.siteName} OpenCode config completed. + echo Reopen OpenCode or the target client to load the new config. + echo ============================================================ +) else ( + echo ============================================================ + echo [LOOK2EYE SETUP FAILED] ${input.siteName} OpenCode config did not complete. Exit code: %LOOK2EYE_SETUP_EXIT% + echo Check the error above. If opencode.json cannot be merged automatically, handle it manually and retry. + echo ============================================================ +) +if "%LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( echo. echo 按任意键关闭窗口... pause >nul ) -endlocal & exit /b %PINAI_SETUP_EXIT% +endlocal & exit /b %LOOK2EYE_SETUP_EXIT% -__PINAI_CLAUDE_CODE_PS1__ +__LOOK2EYE_OPENCODE_PS1__ ${psScript}` } @@ -997,6 +1525,10 @@ export function buildAPIKeyConfigScript(input: ConfigScriptInput): { filename: s ? os === 'win' ? buildClaudeBatchScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName, platform: input.platform }) : buildClaudeShellScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName, platform: input.platform }) + : input.client === 'opencode' + ? os === 'win' + ? buildOpenCodeBatchScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName }) + : buildOpenCodeShellScript({ baseUrl: input.baseUrl, apiKey: input.apiKey, siteName }) : os === 'win' ? buildBatchScript(buildPayload({ ...input, siteName }), siteName) : buildShellScript(buildPayload({ ...input, siteName }), siteName) diff --git a/frontend/src/views/user/KeysView.vue b/frontend/src/views/user/KeysView.vue index 31d4b053294..5550861d0c1 100644 --- a/frontend/src/views/user/KeysView.vue +++ b/frontend/src/views/user/KeysView.vue @@ -1601,7 +1601,7 @@ const downloadConfigScript = (client: ConfigScriptClient) => { allowMessagesDispatch: key.group.allow_messages_dispatch || false, baseUrl: publicSettings.value?.api_base_url || window.location.origin, apiKey: key.key, - siteName: publicSettings.value?.site_name || 'Sub2API' + siteName: publicSettings.value?.site_name || 'look2eye' }) appStore.showSuccess(t('keys.configScriptMenu.downloadStarted')) closeConfigScriptMenu() From 3a993f17604e873387e1efeb8afe3d14efd57ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 16:08:33 +0800 Subject: [PATCH 05/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/__tests__/configScriptDownload.spec.ts | 12 ++++++------ frontend/src/utils/configScriptDownload.ts | 15 +++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index 2d11e2f7b55..ad2717b987a 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -39,7 +39,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-codex-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') expect(script.content).toContain('look2eye Codex CLI 一键配置') - expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") + expect(script.content).toContain("BASE_URL='https://api.look2eye.com'") expect(script.content).toContain("API_KEY='sk-test-key'") expect(script.content).toContain('CONFIG_PATH="$CODEX_DIR/config.toml"') expect(script.content).toContain('AUTH_PATH="$CODEX_DIR/auth.json"') @@ -65,7 +65,7 @@ describe('configScriptDownload', () => { expect(script.content).toContain('LOOK2EYE_CODEX_EXIT') const payload = decodeBatchPayload(script.content) - expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") + expect(payload).toContain("$BaseUrl = 'https://api.look2eye.com'") expect(payload).toContain("$ApiKey = 'sk-win-key'") expect(payload).toContain('$ConfigTomlTemplate') expect(payload).toContain('model_provider = "Look2eye"') @@ -86,7 +86,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-claude-code-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') expect(script.content).toContain('Look2eye Claude Code 配置已完成') - expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") + expect(script.content).toContain("BASE_URL='https://api.look2eye.com'") expect(script.content).toContain("API_KEY='sk-claude-key'") expect(script.content).toContain('CLAUDE_DIR="$HOME/.claude"') expect(script.content).toContain('SETTINGS_PATH="$CLAUDE_DIR/settings.json"') @@ -116,7 +116,7 @@ describe('configScriptDownload', () => { const payload = extractClaudeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName Claude Code config") - expect(payload).toContain('https://api.example.com/antigravity/v1') + expect(payload).toContain('https://api.look2eye.com') expect(payload).toContain('sk-claude-key') expect(payload).toContain('ConvertTo-OrderedMap') expect(payload).toContain('Join-Path $targetHome ".claude"') @@ -140,7 +140,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-opencode-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') expect(script.content).toContain('Look2eye OpenCode 配置已完成') - expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") + expect(script.content).toContain("BASE_URL='https://api.look2eye.com'") expect(script.content).toContain("API_KEY='sk-opencode-key'") expect(script.content).toContain('OPENCODE_DIR="$HOME/.config/opencode"') expect(script.content).toContain('CONFIG_PATH="$OPENCODE_DIR/opencode.json"') @@ -173,7 +173,7 @@ describe('configScriptDownload', () => { expect(payload).toContain("Join-Path (Join-Path $targetHome \".config\") \"opencode\"") expect(payload).toContain('Join-Path $openCodeDir "opencode.json"') expect(payload).toContain('Join-Path $openCodeDir "backups"') - expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") + expect(payload).toContain("$BaseUrl = 'https://api.look2eye.com'") expect(payload).toContain("$ApiKey = 'sk-opencode-win-key'") expect(payload).toContain('$ModelsJson') expect(payload).toContain('"gpt-5.5"') diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index 6c940df8d3d..03932894c29 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -1,6 +1,7 @@ import type { GroupPlatform } from '@/types' export const CONFIG_SCRIPT_SITE_NAME = 'look2eye' +const CONFIG_SCRIPT_BASE_URL = 'https://api.look2eye.com' export type ConfigScriptClient = 'codex' | 'claude' | 'opencode' export type ConfigScriptOS = 'mac' | 'win' @@ -270,7 +271,7 @@ sandbox = "elevated" function buildCodexShellScript(input: Required>): string { const siteName = input.siteName - const baseUrl = resolveBaseUrl(input.baseUrl) + const baseUrl = CONFIG_SCRIPT_BASE_URL const template = codexConfigTemplate(input) return `#!/usr/bin/env sh @@ -498,7 +499,7 @@ stop_codex_processes function buildCodexPowerShellPayload(input: Required>): string { const siteName = input.siteName - const baseUrl = resolveBaseUrl(input.baseUrl) + const baseUrl = CONFIG_SCRIPT_BASE_URL const template = codexConfigTemplate(input) return `$ErrorActionPreference = "Stop" @@ -704,10 +705,8 @@ endlocal & exit /b %LOOK2EYE_CODEX_EXIT% ` } -function resolveClaudeBase(input: Required> & Pick): string { - return input.platform === 'antigravity' - ? resolveAntigravityBase(input.baseUrl) - : resolveBaseUrl(input.baseUrl) +function resolveClaudeBase(_input: Required> & Pick): string { + return CONFIG_SCRIPT_BASE_URL } function buildClaudeShellScript(input: Required> & Pick): string { @@ -981,7 +980,7 @@ ${psScript}` function buildOpenCodeShellScript(input: Required>): string { const siteName = input.siteName - const baseUrl = resolveBaseUrl(input.baseUrl) + const baseUrl = CONFIG_SCRIPT_BASE_URL return `#!/usr/bin/env sh set -eu @@ -1121,7 +1120,7 @@ echo "Done. ${siteName} OpenCode config has been updated." function buildOpenCodePowerShellPayload(input: Required>): string { const siteName = input.siteName - const baseUrl = resolveBaseUrl(input.baseUrl) + const baseUrl = CONFIG_SCRIPT_BASE_URL return `$ErrorActionPreference = "Stop" From 28c82ac430af5319d737e6c089b8f9b517acd244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 16:27:39 +0800 Subject: [PATCH 06/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/utils/__tests__/configScriptDownload.spec.ts | 12 ++++++------ frontend/src/utils/configScriptDownload.ts | 11 +++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index ad2717b987a..35b25eedd21 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -39,7 +39,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-codex-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') expect(script.content).toContain('look2eye Codex CLI 一键配置') - expect(script.content).toContain("BASE_URL='https://api.look2eye.com'") + expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") expect(script.content).toContain("API_KEY='sk-test-key'") expect(script.content).toContain('CONFIG_PATH="$CODEX_DIR/config.toml"') expect(script.content).toContain('AUTH_PATH="$CODEX_DIR/auth.json"') @@ -65,7 +65,7 @@ describe('configScriptDownload', () => { expect(script.content).toContain('LOOK2EYE_CODEX_EXIT') const payload = decodeBatchPayload(script.content) - expect(payload).toContain("$BaseUrl = 'https://api.look2eye.com'") + expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") expect(payload).toContain("$ApiKey = 'sk-win-key'") expect(payload).toContain('$ConfigTomlTemplate') expect(payload).toContain('model_provider = "Look2eye"') @@ -86,7 +86,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-claude-code-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') expect(script.content).toContain('Look2eye Claude Code 配置已完成') - expect(script.content).toContain("BASE_URL='https://api.look2eye.com'") + expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") expect(script.content).toContain("API_KEY='sk-claude-key'") expect(script.content).toContain('CLAUDE_DIR="$HOME/.claude"') expect(script.content).toContain('SETTINGS_PATH="$CLAUDE_DIR/settings.json"') @@ -116,7 +116,7 @@ describe('configScriptDownload', () => { const payload = extractClaudeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName Claude Code config") - expect(payload).toContain('https://api.look2eye.com') + expect(payload).toContain('https://api.example.com/v1') expect(payload).toContain('sk-claude-key') expect(payload).toContain('ConvertTo-OrderedMap') expect(payload).toContain('Join-Path $targetHome ".claude"') @@ -140,7 +140,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-opencode-config.sh') expect(script.content).toContain('#!/usr/bin/env sh') expect(script.content).toContain('Look2eye OpenCode 配置已完成') - expect(script.content).toContain("BASE_URL='https://api.look2eye.com'") + expect(script.content).toContain("BASE_URL='https://api.example.com/v1'") expect(script.content).toContain("API_KEY='sk-opencode-key'") expect(script.content).toContain('OPENCODE_DIR="$HOME/.config/opencode"') expect(script.content).toContain('CONFIG_PATH="$OPENCODE_DIR/opencode.json"') @@ -173,7 +173,7 @@ describe('configScriptDownload', () => { expect(payload).toContain("Join-Path (Join-Path $targetHome \".config\") \"opencode\"") expect(payload).toContain('Join-Path $openCodeDir "opencode.json"') expect(payload).toContain('Join-Path $openCodeDir "backups"') - expect(payload).toContain("$BaseUrl = 'https://api.look2eye.com'") + expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") expect(payload).toContain("$ApiKey = 'sk-opencode-win-key'") expect(payload).toContain('$ModelsJson') expect(payload).toContain('"gpt-5.5"') diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index 03932894c29..1264adbf60b 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -1,7 +1,6 @@ import type { GroupPlatform } from '@/types' export const CONFIG_SCRIPT_SITE_NAME = 'look2eye' -const CONFIG_SCRIPT_BASE_URL = 'https://api.look2eye.com' export type ConfigScriptClient = 'codex' | 'claude' | 'opencode' export type ConfigScriptOS = 'mac' | 'win' @@ -271,7 +270,7 @@ sandbox = "elevated" function buildCodexShellScript(input: Required>): string { const siteName = input.siteName - const baseUrl = CONFIG_SCRIPT_BASE_URL + const baseUrl = resolveBaseUrl(input.baseUrl) const template = codexConfigTemplate(input) return `#!/usr/bin/env sh @@ -499,7 +498,7 @@ stop_codex_processes function buildCodexPowerShellPayload(input: Required>): string { const siteName = input.siteName - const baseUrl = CONFIG_SCRIPT_BASE_URL + const baseUrl = resolveBaseUrl(input.baseUrl) const template = codexConfigTemplate(input) return `$ErrorActionPreference = "Stop" @@ -706,7 +705,7 @@ endlocal & exit /b %LOOK2EYE_CODEX_EXIT% } function resolveClaudeBase(_input: Required> & Pick): string { - return CONFIG_SCRIPT_BASE_URL + return resolveBaseUrl(_input.baseUrl) } function buildClaudeShellScript(input: Required> & Pick): string { @@ -980,7 +979,7 @@ ${psScript}` function buildOpenCodeShellScript(input: Required>): string { const siteName = input.siteName - const baseUrl = CONFIG_SCRIPT_BASE_URL + const baseUrl = resolveBaseUrl(input.baseUrl) return `#!/usr/bin/env sh set -eu @@ -1120,7 +1119,7 @@ echo "Done. ${siteName} OpenCode config has been updated." function buildOpenCodePowerShellPayload(input: Required>): string { const siteName = input.siteName - const baseUrl = CONFIG_SCRIPT_BASE_URL + const baseUrl = resolveBaseUrl(input.baseUrl) return `$ErrorActionPreference = "Stop" From f9ac0f78add820131d9b7fa155e78a7bba02ab79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 16:43:06 +0800 Subject: [PATCH 07/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/configScriptDownload.spec.ts | 18 +++++++++ frontend/src/utils/configScriptDownload.ts | 38 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index 35b25eedd21..8c83a0710ae 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -63,6 +63,12 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-codex-config.bat') expect(script.content).toContain('@echo off') expect(script.content).toContain('LOOK2EYE_CODEX_EXIT') + expect(script.content).toContain('Windows 未检测到 Node.js') + expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') + expect(script.content).toContain('where winget.exe') + expect(script.content).toContain('winget install OpenJS.NodeJS.LTS') + expect(script.content).toContain('https://nodejs.org/') + expect(script.content).toContain('where npm.cmd') const payload = decodeBatchPayload(script.content) expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") @@ -113,6 +119,12 @@ describe('configScriptDownload', () => { expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CLAUDE_CODE_PS1__') expect(script.content).toContain('__LOOK2EYE_CLAUDE_CODE_PS1__') expect(script.content).toContain('LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER') + expect(script.content).toContain('Windows 未检测到 Node.js') + expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') + expect(script.content).toContain('where winget.exe') + expect(script.content).toContain('winget install OpenJS.NodeJS.LTS') + expect(script.content).toContain('https://nodejs.org/') + expect(script.content).toContain('where npm.cmd') const payload = extractClaudeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName Claude Code config") @@ -167,6 +179,12 @@ describe('configScriptDownload', () => { expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_OPENCODE_PS1__') expect(script.content).toContain('__LOOK2EYE_OPENCODE_PS1__') expect(script.content).toContain('LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER') + expect(script.content).toContain('Windows 未检测到 Node.js') + expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') + expect(script.content).toContain('where winget.exe') + expect(script.content).toContain('winget install OpenJS.NodeJS.LTS') + expect(script.content).toContain('https://nodejs.org/') + expect(script.content).toContain('where npm.cmd') const payload = extractOpenCodeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName OpenCode config") diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index 1264adbf60b..ef2ce1260d8 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -679,6 +679,41 @@ try { ` } +function windowsNodeRuntimeNotice(clientLabel: string): string { + return `where node.exe >nul 2>nul +if errorlevel 1 ( + echo. + echo [LOOK2EYE NOTICE] Windows 未检测到 Node.js;配置文件已写入,但 ${clientLabel} 可能无法安装或运行。 + set "LOOK2EYE_INSTALL_NODE=" + set /p "LOOK2EYE_INSTALL_NODE=是否现在通过 winget 安装 Node.js LTS?[Y/N] " + if /i "%LOOK2EYE_INSTALL_NODE%"=="Y" ( + where winget.exe >nul 2>nul + if errorlevel 1 ( + echo 未检测到 winget,无法自动安装 Node.js。 + echo 请手动下载安装 Node.js LTS:https://nodejs.org/ + ) else ( + winget install OpenJS.NodeJS.LTS + where node.exe >nul 2>nul + if errorlevel 1 ( + echo Node.js 安装后当前窗口仍未检测到 node.exe;请重新打开终端后再运行客户端。 + ) else ( + for /f "delims=" %%I in ('node --version 2^>nul') do echo Node.js: %%I + ) + ) + ) else ( + echo 已跳过 Node.js 安装。请稍后安装 Node.js LTS:https://nodejs.org/ + ) +) else ( + for /f "delims=" %%I in ('node --version 2^>nul') do echo Node.js: %%I + where npm.cmd >nul 2>nul + if errorlevel 1 ( + echo [LOOK2EYE NOTICE] 未检测到 npm;如客户端安装失败,请重新安装 Node.js LTS 并勾选 npm。 + ) else ( + for /f "delims=" %%I in ('npm --version 2^>nul') do echo npm: %%I + ) +)` +} + function buildCodexBatchScript(input: Required>): string { const encoded = toBase64UTF8(buildCodexPowerShellPayload(input)) return `@echo off @@ -699,6 +734,7 @@ if "%LOOK2EYE_CODEX_EXIT%"=="0" ( echo Check the error above. If Codex is still open, close it manually and retry. echo ============================================================ ) +${windowsNodeRuntimeNotice('Codex CLI')} pause endlocal & exit /b %LOOK2EYE_CODEX_EXIT% ` @@ -966,6 +1002,7 @@ if "%LOOK2EYE_SETUP_EXIT%"=="0" ( echo Check the error above. If settings.json cannot be merged automatically, handle it manually and retry. echo ============================================================ ) +${windowsNodeRuntimeNotice('Claude Code')} if "%LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( echo. echo 按任意键关闭窗口... @@ -1324,6 +1361,7 @@ if "%LOOK2EYE_SETUP_EXIT%"=="0" ( echo Check the error above. If opencode.json cannot be merged automatically, handle it manually and retry. echo ============================================================ ) +${windowsNodeRuntimeNotice('OpenCode')} if "%LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( echo. echo 按任意键关闭窗口... From 9514a5a333a577fc1e0180d1d711f1f4fd970755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Sun, 7 Jun 2026 18:06:59 +0800 Subject: [PATCH 08/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/configScriptDownload.spec.ts | 36 ++- frontend/src/utils/configScriptDownload.ts | 211 +++++++++--------- 2 files changed, 114 insertions(+), 133 deletions(-) diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index 8c83a0710ae..3aa73990df9 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -5,24 +5,10 @@ import { } from '../configScriptDownload' function decodeBatchPayload(content: string): string { - const encoded = content.match(/FromBase64String\('([^']+)'\)/)?.[1] - if (!encoded) throw new Error('missing encoded payload') - const binary = atob(encoded) - const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)) - return new TextDecoder().decode(bytes) -} - -function extractClaudeBatchPayload(content: string): string { - const marker = '__LOOK2EYE_CLAUDE_CODE_PS1__' - const markerIndex = content.lastIndexOf(marker) - if (markerIndex < 0) throw new Error('missing Claude Code payload marker') - return content.slice(markerIndex + marker.length).trimStart() -} - -function extractOpenCodeBatchPayload(content: string): string { - const marker = '__LOOK2EYE_OPENCODE_PS1__' + const marker = content.match(/(__LOOK2EYE_[A-Z_]+_PS1__)/)?.[1] + if (!marker) throw new Error('missing embedded payload marker') const markerIndex = content.lastIndexOf(marker) - if (markerIndex < 0) throw new Error('missing OpenCode payload marker') + if (markerIndex < 0) throw new Error('missing embedded payload') return content.slice(markerIndex + marker.length).trimStart() } @@ -62,7 +48,11 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-codex-config.bat') expect(script.content).toContain('@echo off') - expect(script.content).toContain('LOOK2EYE_CODEX_EXIT') + expect(script.content).toContain('LOOK2EYE_SETUP_EXIT') + expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CODEX_PS1__') + expect(script.content).toContain('-EncodedCommand ') + expect(script.content).toContain('__LOOK2EYE_CODEX_PS1__') + expect(script.content).not.toContain('$path =') expect(script.content).toContain('Windows 未检测到 Node.js') expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') expect(script.content).toContain('where winget.exe') @@ -116,9 +106,10 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-claude-code-config.bat') expect(script.content).toContain('@echo off') + expect(script.content).toContain('-EncodedCommand ') expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CLAUDE_CODE_PS1__') expect(script.content).toContain('__LOOK2EYE_CLAUDE_CODE_PS1__') - expect(script.content).toContain('LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER') + expect(script.content).not.toContain('$path =') expect(script.content).toContain('Windows 未检测到 Node.js') expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') expect(script.content).toContain('where winget.exe') @@ -126,7 +117,7 @@ describe('configScriptDownload', () => { expect(script.content).toContain('https://nodejs.org/') expect(script.content).toContain('where npm.cmd') - const payload = extractClaudeBatchPayload(script.content) + const payload = decodeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName Claude Code config") expect(payload).toContain('https://api.example.com/v1') expect(payload).toContain('sk-claude-key') @@ -176,9 +167,10 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-opencode-config.bat') expect(script.content).toContain('@echo off') + expect(script.content).toContain('-EncodedCommand ') expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_OPENCODE_PS1__') expect(script.content).toContain('__LOOK2EYE_OPENCODE_PS1__') - expect(script.content).toContain('LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER') + expect(script.content).not.toContain('$path =') expect(script.content).toContain('Windows 未检测到 Node.js') expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') expect(script.content).toContain('where winget.exe') @@ -186,7 +178,7 @@ describe('configScriptDownload', () => { expect(script.content).toContain('https://nodejs.org/') expect(script.content).toContain('where npm.cmd') - const payload = extractOpenCodeBatchPayload(script.content) + const payload = decodeBatchPayload(script.content) expect(payload).toContain("Applying $SiteName OpenCode config") expect(payload).toContain("Join-Path (Join-Path $targetHome \".config\") \"opencode\"") expect(payload).toContain('Join-Path $openCodeDir "opencode.json"') diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index ef2ce1260d8..53f934095a4 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -714,30 +714,82 @@ if errorlevel 1 ( )` } -function buildCodexBatchScript(input: Required>): string { - const encoded = toBase64UTF8(buildCodexPowerShellPayload(input)) +function buildPowerShellPayloadExtractorCommand(): string { + return buildEncodedPowerShellCommand(`$ErrorActionPreference = "Stop" +$scriptPath = $env:LOOK2EYE_SETUP_SCRIPT_PATH +$payloadPath = $env:LOOK2EYE_SETUP_PAYLOAD +$marker = $env:LOOK2EYE_SETUP_MARKER +$text = [System.IO.File]::ReadAllText($scriptPath) +$index = $text.LastIndexOf($marker) +if ($index -lt 0) { throw "PowerShell payload marker not found." } +$payload = $text.Substring($index + $marker.Length).TrimStart([char]13, [char]10) +$encoding = New-Object System.Text.UTF8Encoding($false) +[System.IO.File]::WriteAllText($payloadPath, $payload, $encoding) +`) +} + +function buildEmbeddedPowerShellBatch(input: { + marker: string + payload: string + tempName: string + passArgs?: boolean + successMessage: string + successHint: string + failureMessage: string + failureHint: string + nodeClientLabel: string +}): string { + const extractor = buildPowerShellPayloadExtractorCommand() + const forwardedArgs = input.passArgs ? ' %*' : '' + return `@echo off chcp 65001 >nul setlocal -set "LOOK2EYE_CODEX_EXIT=1" -powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$script = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); $path = Join-Path $env:TEMP 'look2eye-codex-config.ps1'; Set-Content -Encoding UTF8 -Path $path -Value $script; & $path %*" -set "LOOK2EYE_CODEX_EXIT=%ERRORLEVEL%" +set "LOOK2EYE_SETUP_SCRIPT_PATH=%~f0" +set "LOOK2EYE_SETUP_PAYLOAD=%TEMP%\\${input.tempName}-%RANDOM%-%RANDOM%.ps1" +set "LOOK2EYE_SETUP_EXIT=1" +set "LOOK2EYE_SETUP_MARKER=${input.marker}" +powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${extractor} +if errorlevel 1 ( + set "LOOK2EYE_SETUP_EXIT=1" + goto :look2eye_setup_done +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%"${forwardedArgs} +set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" +if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul +:look2eye_setup_done echo. -if "%LOOK2EYE_CODEX_EXIT%"=="0" ( +if "%LOOK2EYE_SETUP_EXIT%"=="0" ( echo ============================================================ - echo [LOOK2EYE SETUP SUCCESS] ${input.siteName} Codex CLI config/restore completed. - echo Reopen Codex or the target client to load the new config. + echo [LOOK2EYE SETUP SUCCESS] ${input.successMessage} + echo ${input.successHint} echo ============================================================ ) else ( echo ============================================================ - echo [LOOK2EYE SETUP FAILED] ${input.siteName} Codex CLI config/restore did not complete. Exit code: %LOOK2EYE_CODEX_EXIT% - echo Check the error above. If Codex is still open, close it manually and retry. + echo [LOOK2EYE SETUP FAILED] ${input.failureMessage} Exit code: %LOOK2EYE_SETUP_EXIT% + echo ${input.failureHint} echo ============================================================ ) -${windowsNodeRuntimeNotice('Codex CLI')} +${windowsNodeRuntimeNotice(input.nodeClientLabel)} pause -endlocal & exit /b %LOOK2EYE_CODEX_EXIT% -` +endlocal & exit /b %LOOK2EYE_SETUP_EXIT% + +${input.marker} +${input.payload}` +} + +function buildCodexBatchScript(input: Required>): string { + return buildEmbeddedPowerShellBatch({ + marker: '__LOOK2EYE_CODEX_PS1__', + payload: buildCodexPowerShellPayload(input), + tempName: 'look2eye-codex-config', + passArgs: true, + successMessage: `${input.siteName} Codex CLI config/restore completed.`, + successHint: 'Reopen Codex or the target client to load the new config.', + failureMessage: `${input.siteName} Codex CLI config/restore did not complete.`, + failureHint: 'Check the error above. If Codex is still open, close it manually and retry.', + nodeClientLabel: 'Codex CLI' + }) } function resolveClaudeBase(_input: Required> & Pick): string { @@ -967,51 +1019,16 @@ try { } function buildClaudeBatchScript(input: Required> & Pick): string { - const psScript = buildClaudePowerShellPayload(input) - return `@echo off -setlocal -set "LOOK2EYE_SETUP_SCRIPT_PATH=%~f0" -set "LOOK2EYE_SETUP_PAYLOAD=" -set "LOOK2EYE_SETUP_EXIT=1" -set "LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER=0" -set "LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CLAUDE_CODE_PS1__" -for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$p=[System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), '.ps1'); Write-Output $p"\`) do set "LOOK2EYE_SETUP_PAYLOAD=%%I" -for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$ErrorActionPreference='Stop'; try { $ps = Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $PID); $cmd = if ($null -ne $ps) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $ps.ParentProcessId) } else { $null }; $parent = if ($null -ne $cmd) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $cmd.ParentProcessId) } else { $null }; if ($null -ne $parent -and $parent.Name -ieq 'explorer.exe') { '1' } else { '0' } } catch { '0' }"\`) do set "LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER=%%I" -if not defined LOOK2EYE_SETUP_PAYLOAD ( - set "LOOK2EYE_SETUP_EXIT=1" - goto :look2eye_setup_done -) -powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$p=$env:LOOK2EYE_SETUP_SCRIPT_PATH; $out=$env:LOOK2EYE_SETUP_PAYLOAD; $marker=$env:LOOK2EYE_SETUP_MARKER; $text=[System.IO.File]::ReadAllText($p); $idx=$text.LastIndexOf($marker); if($idx -lt 0){ Write-Error 'PowerShell payload marker not found.'; exit 1 }; $code=$text.Substring($idx + $marker.Length).TrimStart(); $enc=New-Object System.Text.UTF8Encoding($true); [System.IO.File]::WriteAllText($out, $code, $enc)" -if errorlevel 1 ( - set "LOOK2EYE_SETUP_EXIT=1" - goto :look2eye_setup_done -) -powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%" -set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" -if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul -:look2eye_setup_done -echo. -if "%LOOK2EYE_SETUP_EXIT%"=="0" ( - echo ============================================================ - echo [LOOK2EYE SETUP SUCCESS] ${input.siteName} Claude Code config completed. - echo Reopen Claude Code or the target client to load the new config. - echo ============================================================ -) else ( - echo ============================================================ - echo [LOOK2EYE SETUP FAILED] ${input.siteName} Claude Code config did not complete. Exit code: %LOOK2EYE_SETUP_EXIT% - echo Check the error above. If settings.json cannot be merged automatically, handle it manually and retry. - echo ============================================================ -) -${windowsNodeRuntimeNotice('Claude Code')} -if "%LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( - echo. - echo 按任意键关闭窗口... - pause >nul -) -endlocal & exit /b %LOOK2EYE_SETUP_EXIT% - -__LOOK2EYE_CLAUDE_CODE_PS1__ -${psScript}` + return buildEmbeddedPowerShellBatch({ + marker: '__LOOK2EYE_CLAUDE_CODE_PS1__', + payload: buildClaudePowerShellPayload(input), + tempName: 'look2eye-claude-code-config', + successMessage: `${input.siteName} Claude Code config completed.`, + successHint: 'Reopen Claude Code or the target client to load the new config.', + failureMessage: `${input.siteName} Claude Code config did not complete.`, + failureHint: 'Check the error above. If settings.json cannot be merged automatically, handle it manually and retry.', + nodeClientLabel: 'Claude Code' + }) } function buildOpenCodeShellScript(input: Required>): string { @@ -1326,51 +1343,16 @@ try { } function buildOpenCodeBatchScript(input: Required>): string { - const psScript = buildOpenCodePowerShellPayload(input) - return `@echo off -setlocal -set "LOOK2EYE_SETUP_SCRIPT_PATH=%~f0" -set "LOOK2EYE_SETUP_PAYLOAD=" -set "LOOK2EYE_SETUP_EXIT=1" -set "LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER=0" -set "LOOK2EYE_SETUP_MARKER=__LOOK2EYE_OPENCODE_PS1__" -for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$p=[System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), '.ps1'); Write-Output $p"\`) do set "LOOK2EYE_SETUP_PAYLOAD=%%I" -for /f "usebackq delims=" %%I in (\`powershell.exe -NoProfile -Command "$ErrorActionPreference='Stop'; try { $ps = Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $PID); $cmd = if ($null -ne $ps) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $ps.ParentProcessId) } else { $null }; $parent = if ($null -ne $cmd) { Get-CimInstance Win32_Process -Filter ('ProcessId={0}' -f $cmd.ParentProcessId) } else { $null }; if ($null -ne $parent -and $parent.Name -ieq 'explorer.exe') { '1' } else { '0' } } catch { '0' }"\`) do set "LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER=%%I" -if not defined LOOK2EYE_SETUP_PAYLOAD ( - set "LOOK2EYE_SETUP_EXIT=1" - goto :look2eye_setup_done -) -powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$p=$env:LOOK2EYE_SETUP_SCRIPT_PATH; $out=$env:LOOK2EYE_SETUP_PAYLOAD; $marker=$env:LOOK2EYE_SETUP_MARKER; $text=[System.IO.File]::ReadAllText($p); $idx=$text.LastIndexOf($marker); if($idx -lt 0){ Write-Error 'PowerShell payload marker not found.'; exit 1 }; $code=$text.Substring($idx + $marker.Length).TrimStart(); $enc=New-Object System.Text.UTF8Encoding($true); [System.IO.File]::WriteAllText($out, $code, $enc)" -if errorlevel 1 ( - set "LOOK2EYE_SETUP_EXIT=1" - goto :look2eye_setup_done -) -powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%" -set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" -if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul -:look2eye_setup_done -echo. -if "%LOOK2EYE_SETUP_EXIT%"=="0" ( - echo ============================================================ - echo [LOOK2EYE SETUP SUCCESS] ${input.siteName} OpenCode config completed. - echo Reopen OpenCode or the target client to load the new config. - echo ============================================================ -) else ( - echo ============================================================ - echo [LOOK2EYE SETUP FAILED] ${input.siteName} OpenCode config did not complete. Exit code: %LOOK2EYE_SETUP_EXIT% - echo Check the error above. If opencode.json cannot be merged automatically, handle it manually and retry. - echo ============================================================ -) -${windowsNodeRuntimeNotice('OpenCode')} -if "%LOOK2EYE_SETUP_LAUNCHED_FROM_EXPLORER%"=="1" ( - echo. - echo 按任意键关闭窗口... - pause >nul -) -endlocal & exit /b %LOOK2EYE_SETUP_EXIT% - -__LOOK2EYE_OPENCODE_PS1__ -${psScript}` + return buildEmbeddedPowerShellBatch({ + marker: '__LOOK2EYE_OPENCODE_PS1__', + payload: buildOpenCodePowerShellPayload(input), + tempName: 'look2eye-opencode-config', + successMessage: `${input.siteName} OpenCode config completed.`, + successHint: 'Reopen OpenCode or the target client to load the new config.', + failureMessage: `${input.siteName} OpenCode config did not complete.`, + failureHint: 'Check the error above. If opencode.json cannot be merged automatically, handle it manually and retry.', + nodeClientLabel: 'OpenCode' + }) } function buildClaudePayload(input: Required> & Pick): ConfigPayload { @@ -1516,22 +1498,26 @@ Write-Host "Done. Restart your terminal for environment variable changes to take ` } -function toBase64UTF8(value: string): string { - const bytes = new TextEncoder().encode(value) +function toBase64UTF16LE(value: string): string { let binary = '' - bytes.forEach((byte) => { - binary += String.fromCharCode(byte) - }) + for (let index = 0; index < value.length; index += 1) { + const code = value.charCodeAt(index) + binary += String.fromCharCode(code & 0xff, code >> 8) + } return btoa(binary) } +function buildEncodedPowerShellCommand(script: string): string { + return toBase64UTF16LE(script.replace(/\r\n/g, '\n').replace(/\r/g, '\n')) +} + function buildBatchScript(payload: ConfigPayload, siteName: string): string { const psScript = buildPowerShellScript(payload, siteName) - const encoded = toBase64UTF8(psScript) + const encoded = buildEncodedPowerShellCommand(psScript) return `@echo off setlocal echo Installing ${siteName} ${payload.label} configuration... -powershell -NoProfile -ExecutionPolicy Bypass -Command "$script = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); $path = Join-Path $env:TEMP 'look2eye-config.ps1'; Set-Content -Encoding UTF8 -Path $path -Value $script; & $path" +powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${encoded} if errorlevel 1 ( echo Installation failed. pause @@ -1583,7 +1569,10 @@ export function isConfigScriptClientAvailable(input: Pick Date: Sun, 7 Jun 2026 21:26:01 +0800 Subject: [PATCH 09/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/configScriptDownload.spec.ts | 30 ++- frontend/src/utils/configScriptDownload.ts | 219 +++++++++++------- frontend/src/utils/configScriptTemplates.ts | 121 ++++++++++ 3 files changed, 281 insertions(+), 89 deletions(-) create mode 100644 frontend/src/utils/configScriptTemplates.ts diff --git a/frontend/src/utils/__tests__/configScriptDownload.spec.ts b/frontend/src/utils/__tests__/configScriptDownload.spec.ts index 3aa73990df9..eadbfc242b6 100644 --- a/frontend/src/utils/__tests__/configScriptDownload.spec.ts +++ b/frontend/src/utils/__tests__/configScriptDownload.spec.ts @@ -34,6 +34,8 @@ describe('configScriptDownload', () => { expect(script.content).toContain('base_url = "{{LOOK2EYE_BASE_URL}}"') expect(script.content).toContain('json.dumps({"OPENAI_API_KEY": api_key}') expect(script.content).toContain('stop_codex_processes') + expect(script.content).toContain('ensure_codex_client') + expect(script.content).toContain('npm install -g @openai/codex') }) it('builds a Windows Codex batch script with embedded setup payload', () => { @@ -48,6 +50,7 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('Look2eye-codex-config.bat') expect(script.content).toContain('@echo off') + expect(script.content).toContain('setlocal EnableDelayedExpansion') expect(script.content).toContain('LOOK2EYE_SETUP_EXIT') expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CODEX_PS1__') expect(script.content).toContain('-EncodedCommand ') @@ -55,12 +58,17 @@ describe('configScriptDownload', () => { expect(script.content).not.toContain('$path =') expect(script.content).toContain('Windows 未检测到 Node.js') expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') + expect(script.content).toContain('if /i "!LOOK2EYE_INSTALL_NODE!"=="Y"') + expect(script.content).not.toContain('if /i "%LOOK2EYE_INSTALL_NODE%"=="Y"') expect(script.content).toContain('where winget.exe') - expect(script.content).toContain('winget install OpenJS.NodeJS.LTS') + expect(script.content).toContain('winget install --id OpenJS.NodeJS.LTS --source winget') + expect(script.content).toContain('npm install -g @openai/codex') + expect(script.content).toContain('where codex.cmd') expect(script.content).toContain('https://nodejs.org/') expect(script.content).toContain('where npm.cmd') const payload = decodeBatchPayload(script.content) + expect(payload).not.toContain('\uFEFF') expect(payload).toContain("$BaseUrl = 'https://api.example.com/v1'") expect(payload).toContain("$ApiKey = 'sk-win-key'") expect(payload).toContain('$ConfigTomlTemplate') @@ -93,6 +101,8 @@ describe('configScriptDownload', () => { expect(script.content).toContain('"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"') expect(script.content).toContain('"CLAUDE_CODE_ATTRIBUTION_HEADER": "0"') expect(script.content).not.toContain('.look2eye/claude-code.env') + expect(script.content).toContain('ensure_claude_client') + expect(script.content).toContain('npm install -g @anthropic-ai/claude-code') }) it('builds a Windows Claude Code batch script from the marker-based template', () => { @@ -106,18 +116,24 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-claude-code-config.bat') expect(script.content).toContain('@echo off') + expect(script.content).toContain('setlocal EnableDelayedExpansion') expect(script.content).toContain('-EncodedCommand ') expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_CLAUDE_CODE_PS1__') expect(script.content).toContain('__LOOK2EYE_CLAUDE_CODE_PS1__') expect(script.content).not.toContain('$path =') expect(script.content).toContain('Windows 未检测到 Node.js') expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') + expect(script.content).toContain('if /i "!LOOK2EYE_INSTALL_NODE!"=="Y"') + expect(script.content).not.toContain('if /i "%LOOK2EYE_INSTALL_NODE%"=="Y"') expect(script.content).toContain('where winget.exe') - expect(script.content).toContain('winget install OpenJS.NodeJS.LTS') + expect(script.content).toContain('winget install --id OpenJS.NodeJS.LTS --source winget') + expect(script.content).toContain('npm install -g @anthropic-ai/claude-code') + expect(script.content).toContain('where claude.cmd') expect(script.content).toContain('https://nodejs.org/') expect(script.content).toContain('where npm.cmd') const payload = decodeBatchPayload(script.content) + expect(payload).not.toContain('\uFEFF') expect(payload).toContain("Applying $SiteName Claude Code config") expect(payload).toContain('https://api.example.com/v1') expect(payload).toContain('sk-claude-key') @@ -154,6 +170,8 @@ describe('configScriptDownload', () => { expect(script.content).toContain('options["apiKey"] = api_key') expect(script.content).toContain('opts["store"] = False') expect(script.content).not.toContain('Installing Look2eye OpenCode configuration') + expect(script.content).toContain('ensure_opencode_client') + expect(script.content).toContain('npm install -g opencode-ai') }) it('builds a Windows OpenCode batch script from the marker-based template', () => { @@ -167,18 +185,24 @@ describe('configScriptDownload', () => { expect(script.filename).toBe('look2eye-opencode-config.bat') expect(script.content).toContain('@echo off') + expect(script.content).toContain('setlocal EnableDelayedExpansion') expect(script.content).toContain('-EncodedCommand ') expect(script.content).toContain('LOOK2EYE_SETUP_MARKER=__LOOK2EYE_OPENCODE_PS1__') expect(script.content).toContain('__LOOK2EYE_OPENCODE_PS1__') expect(script.content).not.toContain('$path =') expect(script.content).toContain('Windows 未检测到 Node.js') expect(script.content).toContain('是否现在通过 winget 安装 Node.js LTS?[Y/N]') + expect(script.content).toContain('if /i "!LOOK2EYE_INSTALL_NODE!"=="Y"') + expect(script.content).not.toContain('if /i "%LOOK2EYE_INSTALL_NODE%"=="Y"') expect(script.content).toContain('where winget.exe') - expect(script.content).toContain('winget install OpenJS.NodeJS.LTS') + expect(script.content).toContain('winget install --id OpenJS.NodeJS.LTS --source winget') + expect(script.content).toContain('npm install -g opencode-ai') + expect(script.content).toContain('where opencode.cmd') expect(script.content).toContain('https://nodejs.org/') expect(script.content).toContain('where npm.cmd') const payload = decodeBatchPayload(script.content) + expect(payload).not.toContain('\uFEFF') expect(payload).toContain("Applying $SiteName OpenCode config") expect(payload).toContain("Join-Path (Join-Path $targetHome \".config\") \"opencode\"") expect(payload).toContain('Join-Path $openCodeDir "opencode.json"') diff --git a/frontend/src/utils/configScriptDownload.ts b/frontend/src/utils/configScriptDownload.ts index 53f934095a4..039637495d6 100644 --- a/frontend/src/utils/configScriptDownload.ts +++ b/frontend/src/utils/configScriptDownload.ts @@ -1,4 +1,11 @@ import type { GroupPlatform } from '@/types' +import { + WINDOWS_BASIC_BATCH_TEMPLATE, + WINDOWS_CLIENT_INSTALL_NOTICE_TEMPLATE, + WINDOWS_EMBEDDED_POWERSHELL_BATCH_TEMPLATE, + WINDOWS_NODE_RUNTIME_NOTICE_TEMPLATE, + renderConfigScriptTemplate +} from './configScriptTemplates' export const CONFIG_SCRIPT_SITE_NAME = 'look2eye' @@ -26,6 +33,30 @@ interface ConfigPayload { env?: Record } +interface ClientInstallSpec { + label: string + commandName: string + npmPackage: string +} + +const CODEX_INSTALL_SPEC: ClientInstallSpec = { + label: 'Codex CLI', + commandName: 'codex', + npmPackage: '@openai/codex' +} + +const CLAUDE_CODE_INSTALL_SPEC: ClientInstallSpec = { + label: 'Claude Code', + commandName: 'claude', + npmPackage: '@anthropic-ai/claude-code' +} + +const OPENCODE_INSTALL_SPEC: ClientInstallSpec = { + label: 'OpenCode', + commandName: 'opencode', + npmPackage: 'opencode-ai' +} + const CODEX_DEFAULT_MODEL = 'gpt-5.5' const CODEX_DEFAULT_REVIEW_MODEL = 'gpt-5.5' const CODEX_REASONING_EFFORT = 'xhigh' @@ -224,6 +255,34 @@ function shellQuote(value: string): string { return `'${value.replace(/'/g, `'\\''`)}'` } +function shellClientInstallNotice(spec: ClientInstallSpec): string { + return ` +ensure_${spec.commandName}_client() { + if command -v ${spec.commandName} >/dev/null 2>&1; then + ${spec.commandName} --version 2>/dev/null || true + return 0 + fi + + echo "[LOOK2EYE NOTICE] 未检测到 ${spec.label},准备通过 npm 全局安装。" + if ! command -v npm >/dev/null 2>&1; then + echo "未检测到 npm,无法自动安装 ${spec.label}。请先安装 Node.js LTS:https://nodejs.org/" >&2 + echo "安装 Node.js 后可手动执行:npm install -g ${spec.npmPackage}" >&2 + return 0 + fi + + if npm install -g ${spec.npmPackage}; then + if command -v ${spec.commandName} >/dev/null 2>&1; then + ${spec.commandName} --version 2>/dev/null || true + else + echo "${spec.label} 已安装,但当前终端仍未检测到 ${spec.commandName};请重新打开终端后再运行客户端。" >&2 + fi + else + echo "${spec.label} 自动安装失败,请手动执行:npm install -g ${spec.npmPackage}" >&2 + fi +} +` +} + function powershellSingleQuote(value: string): string { return `'${value.replace(/'/g, "''")}'` } @@ -272,6 +331,7 @@ function buildCodexShellScript(input: Requirednul 2>nul -if errorlevel 1 ( - echo. - echo [LOOK2EYE NOTICE] Windows 未检测到 Node.js;配置文件已写入,但 ${clientLabel} 可能无法安装或运行。 - set "LOOK2EYE_INSTALL_NODE=" - set /p "LOOK2EYE_INSTALL_NODE=是否现在通过 winget 安装 Node.js LTS?[Y/N] " - if /i "%LOOK2EYE_INSTALL_NODE%"=="Y" ( - where winget.exe >nul 2>nul - if errorlevel 1 ( - echo 未检测到 winget,无法自动安装 Node.js。 - echo 请手动下载安装 Node.js LTS:https://nodejs.org/ - ) else ( - winget install OpenJS.NodeJS.LTS - where node.exe >nul 2>nul - if errorlevel 1 ( - echo Node.js 安装后当前窗口仍未检测到 node.exe;请重新打开终端后再运行客户端。 - ) else ( - for /f "delims=" %%I in ('node --version 2^>nul') do echo Node.js: %%I - ) - ) - ) else ( - echo 已跳过 Node.js 安装。请稍后安装 Node.js LTS:https://nodejs.org/ - ) -) else ( - for /f "delims=" %%I in ('node --version 2^>nul') do echo Node.js: %%I - where npm.cmd >nul 2>nul - if errorlevel 1 ( - echo [LOOK2EYE NOTICE] 未检测到 npm;如客户端安装失败,请重新安装 Node.js LTS 并勾选 npm。 - ) else ( - for /f "delims=" %%I in ('npm --version 2^>nul') do echo npm: %%I - ) -)` + return renderConfigScriptTemplate(WINDOWS_NODE_RUNTIME_NOTICE_TEMPLATE, { + CLIENT_LABEL: clientLabel + }) +} + +function windowsClientInstallNotice(spec: ClientInstallSpec): string { + const commandName = spec.commandName.toLowerCase().endsWith('.cmd') + ? spec.commandName + : `${spec.commandName}.cmd` + return renderConfigScriptTemplate(WINDOWS_CLIENT_INSTALL_NOTICE_TEMPLATE, { + CLIENT_LABEL: spec.label, + COMMAND_NAME: commandName, + NPM_PACKAGE: spec.npmPackage + }) } function buildPowerShellPayloadExtractorCommand(): string { @@ -719,12 +763,33 @@ function buildPowerShellPayloadExtractorCommand(): string { $scriptPath = $env:LOOK2EYE_SETUP_SCRIPT_PATH $payloadPath = $env:LOOK2EYE_SETUP_PAYLOAD $marker = $env:LOOK2EYE_SETUP_MARKER -$text = [System.IO.File]::ReadAllText($scriptPath) -$index = $text.LastIndexOf($marker) +$bytes = [System.IO.File]::ReadAllBytes($scriptPath) +$markerBytes = [System.Text.Encoding]::ASCII.GetBytes($marker) +$index = -1 +for ($i = $bytes.Length - $markerBytes.Length; $i -ge 0; $i--) { + $matched = $true + for ($j = 0; $j -lt $markerBytes.Length; $j++) { + if ($bytes[$i + $j] -ne $markerBytes[$j]) { + $matched = $false + break + } + } + if ($matched) { + $index = $i + break + } +} if ($index -lt 0) { throw "PowerShell payload marker not found." } -$payload = $text.Substring($index + $marker.Length).TrimStart([char]13, [char]10) -$encoding = New-Object System.Text.UTF8Encoding($false) -[System.IO.File]::WriteAllText($payloadPath, $payload, $encoding) +$start = $index + $markerBytes.Length +while ($start -lt $bytes.Length -and ($bytes[$start] -eq 13 -or $bytes[$start] -eq 10)) { + $start++ +} +$bom = [byte[]](0xEF, 0xBB, 0xBF) +$payloadLength = $bytes.Length - $start +$output = New-Object byte[] ($bom.Length + $payloadLength) +[System.Array]::Copy($bom, 0, $output, 0, $bom.Length) +[System.Array]::Copy($bytes, $start, $output, $bom.Length, $payloadLength) +[System.IO.File]::WriteAllBytes($payloadPath, $output) `) } @@ -738,44 +803,24 @@ function buildEmbeddedPowerShellBatch(input: { failureMessage: string failureHint: string nodeClientLabel: string + clientInstall: ClientInstallSpec }): string { const extractor = buildPowerShellPayloadExtractorCommand() const forwardedArgs = input.passArgs ? ' %*' : '' - return `@echo off -chcp 65001 >nul -setlocal -set "LOOK2EYE_SETUP_SCRIPT_PATH=%~f0" -set "LOOK2EYE_SETUP_PAYLOAD=%TEMP%\\${input.tempName}-%RANDOM%-%RANDOM%.ps1" -set "LOOK2EYE_SETUP_EXIT=1" -set "LOOK2EYE_SETUP_MARKER=${input.marker}" -powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${extractor} -if errorlevel 1 ( - set "LOOK2EYE_SETUP_EXIT=1" - goto :look2eye_setup_done -) -powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%"${forwardedArgs} -set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" -if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul -:look2eye_setup_done -echo. -if "%LOOK2EYE_SETUP_EXIT%"=="0" ( - echo ============================================================ - echo [LOOK2EYE SETUP SUCCESS] ${input.successMessage} - echo ${input.successHint} - echo ============================================================ -) else ( - echo ============================================================ - echo [LOOK2EYE SETUP FAILED] ${input.failureMessage} Exit code: %LOOK2EYE_SETUP_EXIT% - echo ${input.failureHint} - echo ============================================================ -) -${windowsNodeRuntimeNotice(input.nodeClientLabel)} -pause -endlocal & exit /b %LOOK2EYE_SETUP_EXIT% - -${input.marker} -${input.payload}` + return renderConfigScriptTemplate(WINDOWS_EMBEDDED_POWERSHELL_BATCH_TEMPLATE, { + TEMP_NAME: input.tempName, + MARKER: input.marker, + EXTRACTOR_COMMAND: extractor, + FORWARDED_ARGS: forwardedArgs, + SUCCESS_MESSAGE: input.successMessage, + SUCCESS_HINT: input.successHint, + FAILURE_MESSAGE: input.failureMessage, + FAILURE_HINT: input.failureHint, + NODE_RUNTIME_NOTICE: windowsNodeRuntimeNotice(input.nodeClientLabel), + CLIENT_INSTALL_NOTICE: windowsClientInstallNotice(input.clientInstall), + POWERSHELL_PAYLOAD: input.payload + }) } function buildCodexBatchScript(input: Required>): string { @@ -788,7 +833,8 @@ function buildCodexBatchScript(input: Required> function buildClaudeShellScript(input: Required> & Pick): string { const siteName = input.siteName const baseUrl = resolveClaudeBase(input) + const clientInstall = shellClientInstallNotice(CLAUDE_CODE_INSTALL_SPEC) return `#!/usr/bin/env sh set -eu @@ -900,6 +947,8 @@ settings_path.write_text(json.dumps(settings, indent=2, ensure_ascii=False) + "\ PY echo "Done. ${siteName} Claude Code config has been updated." +${clientInstall} +ensure_claude_client ` } @@ -1027,13 +1076,15 @@ function buildClaudeBatchScript(input: Required>): string { const siteName = input.siteName const baseUrl = resolveBaseUrl(input.baseUrl) + const clientInstall = shellClientInstallNotice(OPENCODE_INSTALL_SPEC) return `#!/usr/bin/env sh set -eu @@ -1168,6 +1219,8 @@ config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\\n", PY echo "Done. ${siteName} OpenCode config has been updated." +${clientInstall} +ensure_opencode_client ` } @@ -1351,7 +1404,8 @@ function buildOpenCodeBatchScript(input: Required + +export function renderConfigScriptTemplate(template: string, values: ConfigScriptTemplateValues): string { + return template.replace(/\{\{([A-Z0-9_]+)\}\}/g, (match, key: string) => { + if (Object.prototype.hasOwnProperty.call(values, key)) { + return values[key] + } + return match + }) +} + +export const WINDOWS_NODE_RUNTIME_NOTICE_TEMPLATE = `where node.exe >nul 2>nul +if errorlevel 1 ( + echo. + echo [LOOK2EYE NOTICE] Windows 未检测到 Node.js;配置文件已写入,但 {{CLIENT_LABEL}} 可能无法安装或运行。 + set "LOOK2EYE_INSTALL_NODE=" + set /p "LOOK2EYE_INSTALL_NODE=是否现在通过 winget 安装 Node.js LTS?[Y/N] " + if /i "!LOOK2EYE_INSTALL_NODE!"=="Y" ( + where winget.exe >nul 2>nul + if errorlevel 1 ( + echo 未检测到 winget,无法自动安装 Node.js。 + echo 请手动下载安装 Node.js LTS:https://nodejs.org/ + ) else ( + winget install --id OpenJS.NodeJS.LTS --source winget --accept-source-agreements --accept-package-agreements + where node.exe >nul 2>nul + if errorlevel 1 ( + echo Node.js 安装后当前窗口仍未检测到 node.exe;请重新打开终端后再运行客户端。 + ) else ( + for /f "delims=" %%I in ('node --version 2^>nul') do echo Node.js: %%I + ) + ) + ) else ( + echo 已跳过 Node.js 安装。请稍后安装 Node.js LTS:https://nodejs.org/ + ) +) else ( + for /f "delims=" %%I in ('node --version 2^>nul') do echo Node.js: %%I + where npm.cmd >nul 2>nul + if errorlevel 1 ( + echo [LOOK2EYE NOTICE] 未检测到 npm;如客户端安装失败,请重新安装 Node.js LTS 并勾选 npm。 + ) else ( + for /f "delims=" %%I in ('npm --version 2^>nul') do echo npm: %%I + ) +)` + +export const WINDOWS_CLIENT_INSTALL_NOTICE_TEMPLATE = `set "LOOK2EYE_CLIENT_FOUND=0" +where {{COMMAND_NAME}} >nul 2>nul +if not errorlevel 1 set "LOOK2EYE_CLIENT_FOUND=1" +if "!LOOK2EYE_CLIENT_FOUND!"=="0" ( + echo. + echo [LOOK2EYE NOTICE] Windows 未检测到 {{CLIENT_LABEL}},准备通过 npm 全局安装。 + where npm.cmd >nul 2>nul + if errorlevel 1 ( + echo 未检测到 npm,无法自动安装 {{CLIENT_LABEL}}。 + echo 请安装 Node.js LTS 后重新运行本脚本,或手动执行:npm install -g {{NPM_PACKAGE}} + ) else ( + npm install -g {{NPM_PACKAGE}} + if errorlevel 1 ( + echo {{CLIENT_LABEL}} 自动安装失败,请手动执行:npm install -g {{NPM_PACKAGE}} + ) else ( + where {{COMMAND_NAME}} >nul 2>nul + if errorlevel 1 ( + echo {{CLIENT_LABEL}} 已安装,但当前窗口仍未检测到 {{COMMAND_NAME}};请重新打开终端后再运行客户端。 + ) else ( + for /f "delims=" %%I in ('{{COMMAND_NAME}} --version 2^>nul') do echo {{CLIENT_LABEL}}: %%I + ) + ) + ) +) else ( + for /f "delims=" %%I in ('{{COMMAND_NAME}} --version 2^>nul') do echo {{CLIENT_LABEL}}: %%I +)` + +export const WINDOWS_EMBEDDED_POWERSHELL_BATCH_TEMPLATE = `@echo off +chcp 65001 >nul +setlocal EnableDelayedExpansion +set "LOOK2EYE_SETUP_SCRIPT_PATH=%~f0" +set "LOOK2EYE_SETUP_PAYLOAD=%TEMP%\\{{TEMP_NAME}}-%RANDOM%-%RANDOM%.ps1" +set "LOOK2EYE_SETUP_EXIT=1" +set "LOOK2EYE_SETUP_MARKER={{MARKER}}" +powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand {{EXTRACTOR_COMMAND}} +if errorlevel 1 ( + set "LOOK2EYE_SETUP_EXIT=1" + goto :look2eye_setup_done +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LOOK2EYE_SETUP_PAYLOAD%"{{FORWARDED_ARGS}} +set "LOOK2EYE_SETUP_EXIT=%ERRORLEVEL%" +if exist "%LOOK2EYE_SETUP_PAYLOAD%" del /f /q "%LOOK2EYE_SETUP_PAYLOAD%" >nul 2>nul +:look2eye_setup_done +echo. +if "%LOOK2EYE_SETUP_EXIT%"=="0" ( + echo ============================================================ + echo [LOOK2EYE SETUP SUCCESS] {{SUCCESS_MESSAGE}} + echo {{SUCCESS_HINT}} + echo ============================================================ +) else ( + echo ============================================================ + echo [LOOK2EYE SETUP FAILED] {{FAILURE_MESSAGE}} Exit code: %LOOK2EYE_SETUP_EXIT% + echo {{FAILURE_HINT}} + echo ============================================================ +) +if "%LOOK2EYE_SETUP_EXIT%"=="0" ( +{{NODE_RUNTIME_NOTICE}} +{{CLIENT_INSTALL_NOTICE}} +) +pause +endlocal & exit /b %LOOK2EYE_SETUP_EXIT% + +{{MARKER}} +{{POWERSHELL_PAYLOAD}}` + +export const WINDOWS_BASIC_BATCH_TEMPLATE = `@echo off +setlocal EnableDelayedExpansion +echo Installing {{SITE_NAME}} {{PAYLOAD_LABEL}} configuration... +powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand {{ENCODED_COMMAND}} +if errorlevel 1 ( + echo Installation failed. + pause + exit /b 1 +) +echo Done. +pause +` From eba25df90bf91736a44455d30298907161f2226b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E9=A1=B6=E5=A4=A9?= <1486157956@qq.com> Date: Wed, 10 Jun 2026 22:44:41 +0800 Subject: [PATCH 10/20] =?UTF-8?q?V0.1.136=20=E5=90=88=E5=B9=B6=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 4 +- .gitignore | 2 + CHANGELOG.md | 10 + Dockerfile | 7 +- README.md | 37 +- README_JA.md | 38 +- assets/partners/logos/RoxyBrowser.png | Bin 0 -> 17201 bytes assets/partners/logos/openmodel.jpg | Bin 0 -> 90965 bytes backend/cmd/server/VERSION | 2 +- backend/cmd/server/wire_gen.go | 3 +- backend/internal/domain/constants.go | 3 + backend/internal/domain/constants_test.go | 36 +- .../handler/admin/admin_service_stub_test.go | 4 + .../handler/admin/compliance_handler.go | 67 + .../internal/handler/admin/group_handler.go | 10 +- .../internal/handler/admin/user_handler.go | 6 + .../user_handler_list_apikey_group_test.go | 53 + backend/internal/handler/gateway_handler.go | 46 +- .../gateway_handler_chat_completions.go | 8 +- .../gateway_handler_error_fallback_test.go | 96 + .../handler/gateway_handler_responses.go | 8 +- backend/internal/handler/handler.go | 1 + .../handler/openai_gateway_handler.go | 44 +- .../handler/openai_gateway_handler_test.go | 35 + backend/internal/handler/page_handler.go | 1 + backend/internal/handler/wire.go | 3 + .../internal/pkg/antigravity/claude_types.go | 1 + .../pkg/antigravity/claude_types_test.go | 1 + .../pkg/antigravity/request_transformer.go | 1 + backend/internal/pkg/claude/constants.go | 6 + backend/internal/repository/user_repo.go | 11 + ...po_apikey_group_filter_integration_test.go | 174 + .../server/middleware/admin_compliance.go | 53 + .../middleware/admin_compliance_test.go | 82 + backend/internal/server/router.go | 2 +- backend/internal/server/routes/admin.go | 14 + backend/internal/server/routes/payment.go | 1 + backend/internal/service/admin_compliance.go | 161 + .../internal/service/admin_compliance_test.go | 133 + backend/internal/service/admin_service.go | 10 + .../service/antigravity_gateway_service.go | 4 + .../service/antigravity_model_mapping_test.go | 7 + backend/internal/service/bedrock_request.go | 35 +- .../internal/service/bedrock_request_test.go | 35 +- .../service/error_passthrough_runtime_test.go | 84 + .../gateway_forward_as_chat_completions.go | 1 + .../service/gateway_forward_as_responses.go | 1 + backend/internal/service/gateway_service.go | 72 +- .../service/gemini_messages_compat_service.go | 5 + backend/internal/service/idempotency.go | 13 +- backend/internal/service/idempotency_test.go | 47 + .../openai_gateway_chat_completions.go | 20 +- .../openai_gateway_chat_completions_test.go | 50 +- .../service/openai_gateway_service.go | 9 + .../internal/service/ops_upstream_context.go | 15 + backend/internal/service/user_service.go | 14 +- ...0_account_group_scheduler_indexes_notx.sql | 5 + deploy/DOCKER.md | 18 + deploy/Dockerfile | 12 +- deploy/docker-compose.dev.yml | 4 + deploy/docker-compose.yml | 2 +- docs/legal/admin-compliance.en.md | 49 + docs/legal/admin-compliance.zh.md | 49 + frontend/package.json | 5 - frontend/pnpm-lock.yaml | 9675 ----------------- frontend/src/App.vue | 20 +- frontend/src/api/__tests__/client.spec.ts | 47 + frontend/src/api/admin/compliance.ts | 42 + frontend/src/api/admin/groups.ts | 12 + frontend/src/api/admin/index.ts | 7 +- frontend/src/api/admin/users.ts | 2 + frontend/src/api/client.ts | 17 + .../account/AccountStatusIndicator.vue | 1 + .../components/account/AccountUsageCell.vue | 1 + .../admin/AdminComplianceDialog.vue | 225 + frontend/src/components/common/BaseDialog.vue | 3 + frontend/src/components/keys/UseKeyModal.vue | 16 + .../keys/__tests__/UseKeyModal.spec.ts | 42 + .../__tests__/useModelWhitelist.spec.ts | 4 +- frontend/src/composables/useModelWhitelist.ts | 7 +- frontend/src/i18n/locales/en.ts | 49 +- frontend/src/i18n/locales/zh.ts | 49 +- frontend/src/router/index.ts | 17 +- frontend/src/stores/adminCompliance.ts | 91 + frontend/src/stores/index.ts | 1 + frontend/src/views/admin/ChannelsView.vue | 3 +- frontend/src/views/admin/UsersView.vue | 57 +- .../apiKeyGroupFilterOptions.spec.ts | 93 + .../views/admin/apiKeyGroupFilterOptions.ts | 75 + .../src/views/public/LegalDocumentView.vue | 36 +- frontend/src/views/user/KeysView.vue | 161 - frontend/src/vite-env.d.ts | 5 + 92 files changed, 2467 insertions(+), 10021 deletions(-) create mode 100644 assets/partners/logos/RoxyBrowser.png create mode 100644 assets/partners/logos/openmodel.jpg create mode 100644 backend/internal/handler/admin/compliance_handler.go create mode 100644 backend/internal/handler/admin/user_handler_list_apikey_group_test.go create mode 100644 backend/internal/repository/user_repo_apikey_group_filter_integration_test.go create mode 100644 backend/internal/server/middleware/admin_compliance.go create mode 100644 backend/internal/server/middleware/admin_compliance_test.go create mode 100644 backend/internal/service/admin_compliance.go create mode 100644 backend/internal/service/admin_compliance_test.go create mode 100644 backend/migrations/150_account_group_scheduler_indexes_notx.sql create mode 100644 docs/legal/admin-compliance.en.md create mode 100644 docs/legal/admin-compliance.zh.md delete mode 100644 frontend/pnpm-lock.yaml create mode 100644 frontend/src/api/admin/compliance.ts create mode 100644 frontend/src/components/admin/AdminComplianceDialog.vue create mode 100644 frontend/src/stores/adminCompliance.ts create mode 100644 frontend/src/views/admin/__tests__/apiKeyGroupFilterOptions.spec.ts create mode 100644 frontend/src/views/admin/apiKeyGroupFilterOptions.ts diff --git a/.dockerignore b/.dockerignore index 770145cb709..de8aef12dd6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,8 +9,10 @@ # Documentation *.md -!deploy/DOCKER.md docs/ +!deploy/DOCKER.md +!docs/legal/ +!docs/legal/*.md # IDE .idea/ diff --git a/.gitignore b/.gitignore index cf251f0715a..0f4e3b06250 100644 --- a/.gitignore +++ b/.gitignore @@ -131,6 +131,8 @@ docs/* !docs/PAYMENT.md !docs/PAYMENT_CN.md !docs/ADMIN_PAYMENT_INTEGRATION_API.md +!docs/legal/ +!docs/legal/*.md .serena/ .codex/ frontend/coverage/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 249ce8c969c..52502118f8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## Unreleased +### Fix: Docker 构建失败 + +- **Dockerfile**: 将 pnpm 版本从 v9 升级到 v11,与本地开发环境对齐,解决 lockfile 格式不兼容问题。 +- **Dockerfile**: COPY 阶段补充 `pnpm-workspace.yaml` 和 `.npmrc`,确保容器内 pnpm install 能读到完整配置。 +- **Dockerfile**: 添加 `COPY docs/legal/`,解决前端构建时引用 `docs/legal/*.md` 文件找不到的问题。 +- **frontend/package.json**: 移除 `pnpm.overrides` 字段(pnpm v10+ 不再从此处读取)。 +- **frontend/pnpm-workspace.yaml**: 将 `overrides` 配置迁移至此文件,符合 pnpm v10+ 规范。 +- **frontend/pnpm-lock.yaml**: 重新生成,与新配置位置匹配。 +- **.dockerignore**: 添加 `!docs/legal/` 和 `!docs/legal/*.md` 例外规则,允许 legal 文档进入 Docker 构建上下文。 + - Added a config script download action to each API key row, matching the existing API key action area. - Added a config script dropdown with Codex CLI, Claude Code, and OpenCode options, styled after the provided reference image. - Added automatic OS detection so macOS downloads `.sh` scripts and Windows downloads `.bat` scripts. diff --git a/Dockerfile b/Dockerfile index f9a03a2bf36..996835c3692 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,15 +20,16 @@ FROM ${NODE_IMAGE} AS frontend-builder WORKDIR /app/frontend -# Install pnpm (pinned to v9 to match CI and keep builds reproducible) -RUN corepack enable && corepack prepare pnpm@9 --activate +# Install pnpm (pinned to match local dev version for lockfile compatibility) +RUN corepack enable && corepack prepare pnpm@11 --activate # Install dependencies first (better caching) -COPY frontend/package.json frontend/pnpm-lock.yaml ./ +COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml frontend/.npmrc ./ RUN pnpm install --frozen-lockfile # Copy frontend source and build COPY frontend/ ./ +COPY docs/legal/ /app/docs/legal/ RUN pnpm run build # ----------------------------------------------------------------------------- diff --git a/README.md b/README.md index f722c3e9c25..8260118340b 100644 --- a/README.md +++ b/README.md @@ -12,23 +12,17 @@ **AI API Gateway Platform for Subscription Quota Distribution** -English | [中文](README_CN.md) | [日本語](README_JA.md) +English | [日本語](README_JA.md) -> **Sub2API officially uses only the domains `sub2api.org` and `pincc.ai`. Other websites using the Sub2API name may be third-party deployments or services and are not affiliated with this project. Please verify and exercise your own judgment.** +## ⚠️ Important Notice ---- - -## Demo - -Try Sub2API online: **[https://demo.sub2api.org/](https://demo.sub2api.org/)** - -Demo credentials (shared demo environment; **not** created automatically for self-hosted installs): +Please read the following carefully before using this project: -| Email | Password | -|-------|----------| -| admin@sub2api.org | admin123 | +- **🚨 Terms of Service Risk**: Using this project may violate the terms of service of Anthropic and other upstream providers. Please review the relevant providers' user agreements before use; all risks arising from such use are borne solely by the user. +- **⚖️ Compliant Use**: Use this project only in compliance with the laws and regulations of your country or region. Any unlawful use is strictly prohibited. +- **📖 Disclaimer**: This project is provided for technical learning and research purposes only. The authors assume no liability for account bans, service interruptions, data loss, or any other direct or indirect damages resulting from the use of this project. ## Overview @@ -51,14 +45,10 @@ Sub2API is an AI API gateway platform designed to distribute and manage API quot > [Want to appear here?](mailto:support@pincc.ai) - - - - - - + + @@ -71,11 +61,6 @@ Sub2API is an AI API gateway platform designed to distribute and manage API quot - - - - - @@ -130,6 +115,12 @@ Sub2API is an AI API gateway platform designed to distribute and manage API quot + + + + +
pinccPinCC is the official relay service built on Sub2API, offering stable access to Claude Code, Codex, Gemini and other popular models — ready to use, no deployment or maintenance required.
PackyCodeThanks to PackyCode for sponsoring this project! PackyCode is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more. PackyCode provides special discounts for our software users: register using this link and enter the "sub2api" promo code during first recharge to get 10% off.openmodelOne API, every top model! OpenModel is a production-grade, high-availability AI API gateway that makes your applications truly fast and stable: automatic failover, smart routing to the best-performing channel, and a production-grade SLA. An SLA that far surpasses any single provider — making stability your core competitive advantage. Works directly with Claude Code, Codex, and Gemini CLI. Register via this link to get started.
Thanks to AIGoCode for sponsoring this project! AIGoCode is an all-in-one platform that integrates Claude Code, Codex, and the latest Gemini models, providing you with stable, efficient, and highly cost-effective AI coding services. The platform offers flexible subscription plans, zero risk of account suspension, direct access with no VPN required, and lightning-fast responses. AIGoCode has prepared a special benefit for sub2api users: if you register via this link, you'll receive an extra 10% bonus credit on your first top-up!
APIKEY.FUNThanks to APIKEY.FUN for sponsoring this project! APIKEY.FUN is one of the core contributors to the sub2api open-source project, dedicated to providing open, stable, and cost-effective AI API access. The platform supports API relay services for Claude, OpenAI, Gemini, and other popular models, with pricing starting from as low as 7% of the original rate. Register via the exclusive link: APIKEY to enjoy a permanent 5% discount on all recharges.
silkapi Thanks to SilkAPI for sponsoring this project! SilkAPI is a relay service built on Sub2API, specializing in providing high-speed and stable Codex API relay.
veilxThanks to RoxyBrowser for sponsoring this project! RoxyBrowser RoxyBrowser is the perfect partner for Sub2API: it features a built-in native Roxy AI Agent and high-quality native residential IPs, supports batch automation via simple commands, and significantly boosts security and efficiency for multi-account management! Click this link to sign up and receive a free residential IP package plus a 10% lifetime discount. +
## Ecosystem diff --git a/README_JA.md b/README_JA.md index 99dff422f4f..607f1877f99 100644 --- a/README_JA.md +++ b/README_JA.md @@ -12,23 +12,17 @@ **サブスクリプションクォータ配分のための AI API ゲートウェイプラットフォーム** -[English](README.md) | [中文](README_CN.md) | 日本語 +[English](README.md) | 日本語 -> **Sub2API が公式に使用しているドメインは `sub2api.org` と `pincc.ai` のみです。Sub2API の名称を使用している他のウェブサイトは、サードパーティによるデプロイやサービスであり、本プロジェクトとは一切関係がありません。ご利用の際はご自身で確認・判断をお願いします。** +## ⚠️ 重要なお知らせ ---- - -## デモ - -Sub2API をオンラインでお試しください: **[https://demo.sub2api.org/](https://demo.sub2api.org/)** - -デモ用認証情報(共有デモ環境です。セルフホスト環境では**自動作成されません**): +本プロジェクトをご利用になる前に、以下の内容を必ずよくお読みください: -| メールアドレス | パスワード | -|-------|----------| -| admin@sub2api.org | admin123 | +- **🚨 利用規約のリスク**:本プロジェクトの使用は、Anthropic をはじめとする上流プロバイダーの利用規約に違反する可能性があります。ご利用前に各プロバイダーのユーザー規約を必ずご確認ください。使用により生じるすべてのリスクはユーザーご自身が負うものとします。 +- **⚖️ 法令遵守**:お住まいの国または地域の法令を遵守した上で本プロジェクトをご利用ください。いかなる違法な目的での使用も固く禁じます。 +- **📖 免責事項**:本プロジェクトは技術的な学習および研究の目的でのみ提供されます。本プロジェクトの使用により生じたアカウントの停止、サービスの中断、データの損失、その他一切の直接的または間接的な損害について、作者は一切の責任を負いません。 ## 概要 @@ -51,13 +45,10 @@ Sub2API は、AI 製品のサブスクリプションから API クォータを > [こちらに掲載しませんか?](mailto:support@pincc.ai) + - - - - - - + + @@ -70,11 +61,6 @@ Sub2API は、AI 製品のサブスクリプションから API クォータを - - - - - @@ -131,6 +117,12 @@ Sub2API は、AI 製品のサブスクリプションから API クォータを + + + + +
pinccPinCC は Sub2API 上に構築された公式リレーサービスで、Claude Code、Codex、Gemini などの人気モデルへの安定したアクセスを提供します。デプロイやメンテナンスは不要で、すぐにご利用いただけます。
PackyCodePackyCode のご支援に感謝します!PackyCode は Claude Code、Codex、Gemini などのリレーサービスを提供する信頼性の高い API 中継プラットフォームです。本ソフト利用者向けに特別割引があります:このリンクで登録し、チャージ時に「sub2api」クーポンを入力すると 10% オフになります。openmodel1つの API で、トップモデルを使い放題!OpenModel は本番環境グレードで高可用性の AI API ゲートウェイに特化し、アプリを真に高速・安定させます:自動フェイルオーバー、最適なチャネルへのスマートルーティング、本番グレードの SLA 保証。単一プロバイダーをはるかに上回る SLA で、安定性をあなたの核心的な競争力にします。
AIGoCode のご支援に感謝します!AIGoCode は Claude Code、Codex、最新の Gemini モデルを統合したオールインワンプラットフォームで、安定的かつ効率的でコストパフォーマンスに優れた AI コーディングサービスを提供します。柔軟なサブスクリプションプラン、アカウント停止リスクゼロ、VPN 不要の直接アクセス、超高速レスポンスが特長です。AIGoCode は sub2api ユーザー向けに特別特典を用意しています:こちらのリンクから登録すると、初回チャージ時に 10% のボーナスクレジットを追加プレゼント!
APIKEY.FUNAPIKEY.FUN のご支援に感謝します!APIKEY.FUN は sub2api オープンソースプロジェクトのコアコントリビューターの一つであり、オープンで安定した、コストパフォーマンスに優れた AI API アクセスサービスの提供に取り組んでいます。プラットフォームは Claude、OpenAI、Gemini など人気モデルの API 中継サービスをサポートし、価格は公式料金のわずか 7% から。専用リンク APIKEY から登録すると、すべてのチャージで永久 5% 割引をご利用いただけます。
silkapi SilkAPI のご支援に感謝します!SilkAPI は Sub2API をベースに構築された中継サービスで、高速かつ安定した Codex API 中継の提供に特化しています。
RoxyBrowserRoxyBrowser のご支援に感謝します!RoxyBrowser は Sub2API の理想的なパートナーです:ネイティブ統合された Roxy AI Agent と高品質なネイティブ住宅 IP を搭載し、シンプルなコマンドで一括自動化をサポート、マルチアカウント管理のセキュリティと効率を大幅に向上させます!このリンクから登録すると、無料の住宅 IP パッケージと生涯 10% 割引を獲得できます。 +
## エコシステム diff --git a/assets/partners/logos/RoxyBrowser.png b/assets/partners/logos/RoxyBrowser.png new file mode 100644 index 0000000000000000000000000000000000000000..72091c57401cbf9cb835b9dabc5cab3195c1f8e9 GIT binary patch literal 17201 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 l{`mjtQy>0s9~E)$m*Z9@VPEqA9z}*gJ&to) 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 literal 0 HcmV?d00001 diff --git a/backend/cmd/server/VERSION b/backend/cmd/server/VERSION index 39e1a8efba9..71d61ed6fc6 100644 --- a/backend/cmd/server/VERSION +++ b/backend/cmd/server/VERSION @@ -1 +1 @@ -0.1.135 +0.1.136 diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go index bd6ca2315bb..879d56d22fe 100644 --- a/backend/cmd/server/wire_gen.go +++ b/backend/cmd/server/wire_gen.go @@ -240,7 +240,8 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { contentModerationHandler := admin.NewContentModerationHandler(contentModerationService) paymentHandler := admin.NewPaymentHandler(paymentService, paymentConfigService) affiliateHandler := admin.NewAffiliateHandler(affiliateService, adminService) - adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, backupHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, tlsFingerprintProfileHandler, adminAPIKeyHandler, scheduledTestHandler, channelHandler, channelMonitorHandler, channelMonitorRequestTemplateHandler, contentModerationHandler, paymentHandler, affiliateHandler) + complianceHandler := admin.NewComplianceHandler(settingService) + adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, backupHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, tlsFingerprintProfileHandler, adminAPIKeyHandler, scheduledTestHandler, channelHandler, channelMonitorHandler, channelMonitorRequestTemplateHandler, contentModerationHandler, paymentHandler, affiliateHandler, complianceHandler) usageRecordWorkerPool := service.NewUsageRecordWorkerPool(configConfig) userMsgQueueCache := repository.NewUserMsgQueueCache(redisClient) userMessageQueueService := service.ProvideUserMessageQueueService(userMsgQueueCache, rpmCache, configConfig) diff --git a/backend/internal/domain/constants.go b/backend/internal/domain/constants.go index 7601f35bc95..4ed3f72b3a6 100644 --- a/backend/internal/domain/constants.go +++ b/backend/internal/domain/constants.go @@ -72,6 +72,7 @@ const ( // 与前端 useModelWhitelist.ts 中的 antigravityDefaultMappings 保持一致 var DefaultAntigravityModelMapping = map[string]string{ // Claude 白名单 + "claude-fable-5": "claude-fable-5", // 官方模型 "claude-opus-4-8": "claude-opus-4-8", // 官方模型 "claude-opus-4-7": "claude-opus-4-7", // 官方模型 "claude-opus-4-6-thinking": "claude-opus-4-6-thinking", // 官方模型 @@ -122,6 +123,8 @@ var DefaultAntigravityModelMapping = map[string]string{ // 注意:此处的 "us." 前缀仅为默认值,ResolveBedrockModelID 会根据账号配置的 // aws_region 自动调整为匹配的区域前缀(如 eu.、apac.、jp. 等) var DefaultBedrockModelMapping = map[string]string{ + // Claude Fable + "claude-fable-5": "anthropic.claude-fable-5", // Claude Opus "claude-opus-4-8": "us.anthropic.claude-opus-4-8-v1", "claude-opus-4-7": "us.anthropic.claude-opus-4-7-v1", diff --git a/backend/internal/domain/constants_test.go b/backend/internal/domain/constants_test.go index fe6272c5ac3..0b24aea9150 100644 --- a/backend/internal/domain/constants_test.go +++ b/backend/internal/domain/constants_test.go @@ -25,26 +25,38 @@ func TestDefaultAntigravityModelMapping_ImageCompatibilityAliases(t *testing.T) } } -func TestDefaultAntigravityModelMapping_ContainsOpus48(t *testing.T) { +func TestDefaultAntigravityModelMapping_ContainsNewClaudeModels(t *testing.T) { t.Parallel() - got, ok := DefaultAntigravityModelMapping["claude-opus-4-8"] - if !ok { - t.Fatal("expected mapping for claude-opus-4-8 to exist") + cases := map[string]string{ + "claude-fable-5": "claude-fable-5", + "claude-opus-4-8": "claude-opus-4-8", } - if got != "claude-opus-4-8" { - t.Fatalf("unexpected claude-opus-4-8 mapping: got %q", got) + for from, want := range cases { + got, ok := DefaultAntigravityModelMapping[from] + if !ok { + t.Fatalf("expected mapping for %q to exist", from) + } + if got != want { + t.Fatalf("unexpected mapping for %q: got %q want %q", from, got, want) + } } } -func TestDefaultBedrockModelMapping_ContainsOpus48(t *testing.T) { +func TestDefaultBedrockModelMapping_ContainsNewClaudeModels(t *testing.T) { t.Parallel() - got, ok := DefaultBedrockModelMapping["claude-opus-4-8"] - if !ok { - t.Fatal("expected Bedrock mapping for claude-opus-4-8 to exist") + cases := map[string]string{ + "claude-fable-5": "anthropic.claude-fable-5", + "claude-opus-4-8": "us.anthropic.claude-opus-4-8-v1", } - if got != "us.anthropic.claude-opus-4-8-v1" { - t.Fatalf("unexpected Bedrock claude-opus-4-8 mapping: got %q", got) + for from, want := range cases { + got, ok := DefaultBedrockModelMapping[from] + if !ok { + t.Fatalf("expected Bedrock mapping for %q to exist", from) + } + if got != want { + t.Fatalf("unexpected Bedrock mapping for %q: got %q want %q", from, got, want) + } } } diff --git a/backend/internal/handler/admin/admin_service_stub_test.go b/backend/internal/handler/admin/admin_service_stub_test.go index bc4da86c529..41b9b862893 100644 --- a/backend/internal/handler/admin/admin_service_stub_test.go +++ b/backend/internal/handler/admin/admin_service_stub_test.go @@ -264,6 +264,10 @@ func (s *stubAdminService) GetAllGroupsByPlatform(ctx context.Context, platform return s.groups, nil } +func (s *stubAdminService) GetAllGroupsIncludingInactive(ctx context.Context) ([]service.Group, error) { + return s.groups, nil +} + func (s *stubAdminService) GetGroup(ctx context.Context, id int64) (*service.Group, error) { group := service.Group{ID: id, Name: "group", Status: service.StatusActive} return &group, nil diff --git a/backend/internal/handler/admin/compliance_handler.go b/backend/internal/handler/admin/compliance_handler.go new file mode 100644 index 00000000000..3d4b34b7807 --- /dev/null +++ b/backend/internal/handler/admin/compliance_handler.go @@ -0,0 +1,67 @@ +package admin + +import ( + "strings" + + "github.com/Wei-Shaw/sub2api/internal/pkg/ip" + "github.com/Wei-Shaw/sub2api/internal/pkg/response" + "github.com/Wei-Shaw/sub2api/internal/server/middleware" + "github.com/Wei-Shaw/sub2api/internal/service" + + "github.com/gin-gonic/gin" +) + +type ComplianceHandler struct { + settingService *service.SettingService +} + +func NewComplianceHandler(settingService *service.SettingService) *ComplianceHandler { + return &ComplianceHandler{settingService: settingService} +} + +type AcceptAdminComplianceRequest struct { + Phrase string `json:"phrase" binding:"required"` + Language string `json:"language"` +} + +func (h *ComplianceHandler) GetStatus(c *gin.Context) { + subject, ok := middleware.GetAuthSubjectFromContext(c) + if !ok { + response.Unauthorized(c, "User not authenticated") + return + } + + status, err := h.settingService.GetAdminComplianceStatus(c.Request.Context(), subject.UserID) + if err != nil { + response.ErrorFrom(c, err) + return + } + response.Success(c, status) +} + +func (h *ComplianceHandler) Accept(c *gin.Context) { + var req AcceptAdminComplianceRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "Invalid request: "+err.Error()) + return + } + + subject, ok := middleware.GetAuthSubjectFromContext(c) + if !ok { + response.Unauthorized(c, "User not authenticated") + return + } + + status, err := h.settingService.AcceptAdminCompliance(c.Request.Context(), service.AdminComplianceAcceptInput{ + AdminUserID: subject.UserID, + Phrase: req.Phrase, + Language: req.Language, + IPAddress: ip.GetClientIP(c), + UserAgent: strings.TrimSpace(c.GetHeader("User-Agent")), + }) + if err != nil { + response.ErrorFrom(c, err) + return + } + response.Success(c, status) +} diff --git a/backend/internal/handler/admin/group_handler.go b/backend/internal/handler/admin/group_handler.go index 102ee02fdcd..0468fc34e9b 100644 --- a/backend/internal/handler/admin/group_handler.go +++ b/backend/internal/handler/admin/group_handler.go @@ -196,15 +196,21 @@ func (h *GroupHandler) List(c *gin.Context) { response.Paginated(c, outGroups, total, page, pageSize) } -// GetAll handles getting all active groups without pagination +// GetAll handles getting all active groups without pagination. +// Pass ?include_inactive=true to also include disabled groups (used by the +// API Key group filter, which needs to surface groups that still have API keys +// bound to them even after the group is disabled). // GET /api/v1/admin/groups/all func (h *GroupHandler) GetAll(c *gin.Context) { platform := c.Query("platform") + includeInactive := c.Query("include_inactive") == "true" var groups []service.Group var err error - if platform != "" { + if includeInactive { + groups, err = h.adminService.GetAllGroupsIncludingInactive(c.Request.Context()) + } else if platform != "" { groups, err = h.adminService.GetAllGroupsByPlatform(c.Request.Context(), platform) } else { groups, err = h.adminService.GetAllGroups(c.Request.Context()) diff --git a/backend/internal/handler/admin/user_handler.go b/backend/internal/handler/admin/user_handler.go index a21fe55a938..fb953057aaf 100644 --- a/backend/internal/handler/admin/user_handler.go +++ b/backend/internal/handler/admin/user_handler.go @@ -106,6 +106,7 @@ type BindUserAuthIdentityChannelRequest struct { // - search: search in email, username // - attr[{id}]: filter by custom attribute value, e.g. attr[1]=company // - group_name: fuzzy filter by allowed group name +// - api_key_group_id: filter by the exact group bound to the user's API keys func (h *UserHandler) List(c *gin.Context) { page, pageSize := response.ParsePagination(c) @@ -123,6 +124,11 @@ func (h *UserHandler) List(c *gin.Context) { GroupName: strings.TrimSpace(c.Query("group_name")), Attributes: parseAttributeFilters(c), } + if raw := strings.TrimSpace(c.Query("api_key_group_id")); raw != "" { + if id, parseErr := strconv.ParseInt(raw, 10, 64); parseErr == nil && id > 0 { + filters.APIKeyGroupID = id + } + } sortBy := c.DefaultQuery("sort_by", "created_at") sortOrder := c.DefaultQuery("sort_order", "desc") if raw, ok := c.GetQuery("include_subscriptions"); ok { diff --git a/backend/internal/handler/admin/user_handler_list_apikey_group_test.go b/backend/internal/handler/admin/user_handler_list_apikey_group_test.go new file mode 100644 index 00000000000..b5cdd9a18f9 --- /dev/null +++ b/backend/internal/handler/admin/user_handler_list_apikey_group_test.go @@ -0,0 +1,53 @@ +package admin + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Wei-Shaw/sub2api/internal/service" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/require" +) + +// listUsersFilterStub 捕获传入 ListUsers 的 filters,其余 AdminService 方法走 baseline stub。 +type listUsersFilterStub struct { + service.AdminService + captured service.UserListFilters +} + +func (s *listUsersFilterStub) ListUsers(_ context.Context, _, _ int, filters service.UserListFilters, _, _ string) ([]service.User, int64, error) { + s.captured = filters + return []service.User{}, 0, nil +} + +func TestAdminUserList_ParsesAPIKeyGroupID(t *testing.T) { + gin.SetMode(gin.TestMode) + cases := []struct { + name string + query string + want int64 + }{ + {"valid id", "?api_key_group_id=42", 42}, + {"missing", "", 0}, + {"zero ignored", "?api_key_group_id=0", 0}, + {"negative ignored", "?api_key_group_id=-3", 0}, + {"non-numeric ignored", "?api_key_group_id=abc", 0}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + stub := &listUsersFilterStub{AdminService: newStubAdminService()} + r := gin.New() + h := NewUserHandler(stub, nil, nil, nil) + r.GET("/admin/users", h.List) + + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/admin/users"+tc.query, nil) + r.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code) + require.Equal(t, tc.want, stub.captured.APIKeyGroupID) + }) + } +} diff --git a/backend/internal/handler/gateway_handler.go b/backend/internal/handler/gateway_handler.go index 8853bffbe6a..d0e2c6b7301 100644 --- a/backend/internal/handler/gateway_handler.go +++ b/backend/internal/handler/gateway_handler.go @@ -477,12 +477,17 @@ func (h *GatewayHandler) Messages(c *gin.Context) { return } } - wroteFallback := h.ensureForwardErrorResponse(c, streamStarted) + upstreamErrorAlreadyCommunicated := gatewayForwardErrorAlreadyCommunicated(c, writerSizeBeforeForward, err) + wroteFallback := false + if !upstreamErrorAlreadyCommunicated { + wroteFallback = h.ensureForwardErrorResponse(c, streamStarted) + } forwardFailedFields := []zap.Field{ zap.Int64("account_id", account.ID), zap.String("account_name", account.Name), zap.String("account_platform", account.Platform), zap.Bool("fallback_error_response_written", wroteFallback), + zap.Bool("upstream_error_response_already_written", upstreamErrorAlreadyCommunicated), zap.Error(err), } if account.Proxy != nil { @@ -769,8 +774,8 @@ func (h *GatewayHandler) Messages(c *gin.Context) { return } } - // Bedrock CC 兼容:渠道模型映射后,清理 Anthropic API 专有字段、注入 Bedrock 必需字段 - if err := attemptParsedReq.ReplaceBody(h.gatewayService.ApplyBedrockCCCompat(c.Request.Context(), attemptParsedReq.Body.Bytes(), attemptParsedReq.Model, account, apiKey.GroupID)); err != nil { + // Bedrock CC 兼容:清理 body 专有字段 + 过滤 anthropic-beta header,适用于所有转发路径 + if err := attemptParsedReq.ReplaceBody(h.gatewayService.ApplyBedrockCCCompat(c, attemptParsedReq.Body.Bytes(), attemptParsedReq.Model, account, apiKey.GroupID)); err != nil { h.errorResponse(c, http.StatusBadRequest, "invalid_request_error", "Failed to parse request body") return } @@ -874,12 +879,17 @@ func (h *GatewayHandler) Messages(c *gin.Context) { return } } - wroteFallback := h.ensureForwardErrorResponse(c, streamStarted) + upstreamErrorAlreadyCommunicated := gatewayForwardErrorAlreadyCommunicated(c, writerSizeBeforeForward, err) + wroteFallback := false + if !upstreamErrorAlreadyCommunicated { + wroteFallback = h.ensureForwardErrorResponse(c, streamStarted) + } forwardFailedFields := []zap.Field{ zap.Int64("account_id", account.ID), zap.String("account_name", account.Name), zap.String("account_platform", account.Platform), zap.Bool("fallback_error_response_written", wroteFallback), + zap.Bool("upstream_error_response_already_written", upstreamErrorAlreadyCommunicated), zap.Error(err), } if account.Proxy != nil { @@ -1605,6 +1615,9 @@ func (h *GatewayHandler) ensureForwardErrorResponse(c *gin.Context, streamStarte if c == nil || c.Writer == nil { return false } + if service.IsResponseCommitted(c) { + return false + } if c.Writer.Written() { streamStarted = true } @@ -1612,6 +1625,31 @@ func (h *GatewayHandler) ensureForwardErrorResponse(c *gin.Context, streamStarte return true } +// gatewayForwardErrorAlreadyCommunicated reports whether a Forward implementation +// has already written a complete error response to the client before returning +// an error to the handler. +// +// This is intentionally narrower than "writer size changed": a stream may have +// only emitted keepalive pings or partial data, in which case the handler still +// needs to append a protocol-level terminal error. Non-SSE output from Forward +// is different: service-level helpers such as handleErrorResponse/writeClaudeError +// already wrote the client-visible JSON body, so adding the generic streaming +// fallback would corrupt the response by appending a second `data: ...` frame. +func gatewayForwardErrorAlreadyCommunicated(c *gin.Context, writerSizeBeforeForward int, err error) bool { + if err == nil || c == nil || c.Writer == nil { + return false + } + if c.Writer.Size() == writerSizeBeforeForward { + return false + } + + contentType := strings.ToLower(strings.TrimSpace(c.Writer.Header().Get("Content-Type"))) + if contentType == "" { + return false + } + return !strings.Contains(contentType, "text/event-stream") +} + // checkClaudeCodeVersion 检查 Claude Code 客户端版本是否满足版本要求 // 仅对已识别的 Claude Code 客户端执行,count_tokens 路径除外 func (h *GatewayHandler) checkClaudeCodeVersion(c *gin.Context) bool { diff --git a/backend/internal/handler/gateway_handler_chat_completions.go b/backend/internal/handler/gateway_handler_chat_completions.go index fc034029c45..49347d02f0b 100644 --- a/backend/internal/handler/gateway_handler_chat_completions.go +++ b/backend/internal/handler/gateway_handler_chat_completions.go @@ -281,9 +281,15 @@ func (h *GatewayHandler) ChatCompletions(c *gin.Context) { return } } - h.ensureForwardErrorResponse(c, streamStarted) + upstreamErrorAlreadyCommunicated := gatewayForwardErrorAlreadyCommunicated(c, writerSizeBeforeForward, err) + wroteFallback := false + if !upstreamErrorAlreadyCommunicated { + wroteFallback = h.ensureForwardErrorResponse(c, streamStarted) + } reqLog.Error("gateway.cc.forward_failed", zap.Int64("account_id", account.ID), + zap.Bool("fallback_error_response_written", wroteFallback), + zap.Bool("upstream_error_response_already_written", upstreamErrorAlreadyCommunicated), zap.Error(err), ) return diff --git a/backend/internal/handler/gateway_handler_error_fallback_test.go b/backend/internal/handler/gateway_handler_error_fallback_test.go index fe9e2ebf446..40ba79f7f14 100644 --- a/backend/internal/handler/gateway_handler_error_fallback_test.go +++ b/backend/internal/handler/gateway_handler_error_fallback_test.go @@ -2,8 +2,10 @@ package handler import ( "encoding/json" + "errors" "net/http" "net/http/httptest" + "strings" "testing" "github.com/gin-gonic/gin" @@ -69,3 +71,97 @@ func TestGatewayEnsureForwardErrorResponse_ResponsesRouteAfterWrittenEmitsRespon assert.Contains(t, body, "event: response.failed\n") assert.Contains(t, body, `"type":"response.failed"`) } + +func TestGatewayForwardErrorAlreadyCommunicated(t *testing.T) { + gin.SetMode(gin.TestMode) + + t.Run("json error already written", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointMessages, nil) + before := c.Writer.Size() + c.JSON(http.StatusBadGateway, gin.H{ + "type": "error", + "error": gin.H{ + "type": "upstream_error", + "message": "Your Claude Code version (2.1.39) is below the minimum required version (2.1.81). Please update: npm update -g @anthropic-ai/claude-code", + }, + }) + + reported := gatewayForwardErrorAlreadyCommunicated(c, before, errors.New("upstream error: 400 message=version too low")) + + require.True(t, reported) + body := w.Body.String() + assert.NotContains(t, body, `data: {"type":"error"`) + }) + + t.Run("sse ping still needs fallback", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointMessages, nil) + c.Header("Content-Type", "text/event-stream") + before := c.Writer.Size() + _, _ = c.Writer.WriteString(":\n\n") + + reported := gatewayForwardErrorAlreadyCommunicated(c, before, errors.New("stream read error: unexpected EOF")) + + require.False(t, reported) + }) + + t.Run("no write still needs fallback", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointMessages, nil) + + reported := gatewayForwardErrorAlreadyCommunicated(c, c.Writer.Size(), errors.New("upstream request failed")) + + require.False(t, reported) + }) + + // apikey 场景核心回归:复刻 GatewayService.handleErrorResponse 的 case 400 —— + // 原样透传上游 JSON body 后返回 err。此时错误已经完整告知客户端, + // handler 不得再追加 data:{"type":"error"} 帧,否则响应被污染成「JSON + 一行 data:」。 + t.Run("upstream 400 json passthrough via c.Data", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointMessages, nil) + before := c.Writer.Size() + upstreamBody := []byte(`{"type":"error","error":{"type":"upstream_error","message":"Your Claude Code version (2.1.39) is below the minimum required version (2.1.81). Please update: npm update -g @anthropic-ai/claude-code"}}`) + c.Data(http.StatusBadRequest, "application/json", upstreamBody) + + reported := gatewayForwardErrorAlreadyCommunicated(c, before, errors.New("upstream error: 400 message=version too low")) + + require.True(t, reported) + body := w.Body.String() + assert.NotContains(t, body, `data: {"type":"error"`) + // 客户端只应收到上游那一份错误,没有被追加第二份。 + assert.Equal(t, 1, strings.Count(body, `"type":"error"`)) + }) + + // 流式已开始(已 flush 真实 SSE 事件,不只是 ping)+ 上游中途 400: + // HTTP 200 已固化,仍需 handler 补协议级终止帧,故不算「已完整告知」。 + t.Run("streaming 400 mid-stream still needs fallback", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointMessages, nil) + c.Header("Content-Type", "text/event-stream") + before := c.Writer.Size() + _, _ = c.Writer.WriteString("event: message_start\ndata: {\"type\":\"message_start\"}\n\n") + + reported := gatewayForwardErrorAlreadyCommunicated(c, before, errors.New("upstream error: 400 message=version too low")) + + require.False(t, reported) + }) + + // 防御边界:err 为 nil 时永远不算「已告知」,避免在成功路径误吞兜底逻辑。 + t.Run("nil error never reports communicated", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointMessages, nil) + c.JSON(http.StatusOK, gin.H{"ok": true}) + + reported := gatewayForwardErrorAlreadyCommunicated(c, 0, nil) + + require.False(t, reported) + }) +} diff --git a/backend/internal/handler/gateway_handler_responses.go b/backend/internal/handler/gateway_handler_responses.go index 3edecd77270..141c85ae611 100644 --- a/backend/internal/handler/gateway_handler_responses.go +++ b/backend/internal/handler/gateway_handler_responses.go @@ -260,9 +260,15 @@ func (h *GatewayHandler) Responses(c *gin.Context) { return } } - h.ensureForwardErrorResponse(c, streamStarted) + upstreamErrorAlreadyCommunicated := gatewayForwardErrorAlreadyCommunicated(c, writerSizeBeforeForward, err) + wroteFallback := false + if !upstreamErrorAlreadyCommunicated { + wroteFallback = h.ensureForwardErrorResponse(c, streamStarted) + } reqLog.Error("gateway.responses.forward_failed", zap.Int64("account_id", account.ID), + zap.Bool("fallback_error_response_written", wroteFallback), + zap.Bool("upstream_error_response_already_written", upstreamErrorAlreadyCommunicated), zap.Error(err), ) return diff --git a/backend/internal/handler/handler.go b/backend/internal/handler/handler.go index 308b219921e..95cac6274c1 100644 --- a/backend/internal/handler/handler.go +++ b/backend/internal/handler/handler.go @@ -36,6 +36,7 @@ type AdminHandlers struct { ContentModeration *admin.ContentModerationHandler Payment *admin.PaymentHandler Affiliate *admin.AffiliateHandler + Compliance *admin.ComplianceHandler } // Handlers contains all HTTP handlers diff --git a/backend/internal/handler/openai_gateway_handler.go b/backend/internal/handler/openai_gateway_handler.go index 6730529743b..65bf172ecd4 100644 --- a/backend/internal/handler/openai_gateway_handler.go +++ b/backend/internal/handler/openai_gateway_handler.go @@ -47,6 +47,30 @@ func resolveOpenAIMessagesDispatchMappedModel(apiKey *service.APIKey, requestedM return strings.TrimSpace(apiKey.Group.ResolveMessagesDispatchModel(requestedModel)) } +type openAIModelBodyReplaceFunc func([]byte, string) []byte + +func openAIModelMappedBody(body []byte, mapped bool, mappedModel string, replace openAIModelBodyReplaceFunc) []byte { + if !mapped || replace == nil { + return body + } + return replace(body, mappedModel) +} + +func newOpenAIModelMappedBodyCache(body []byte, replace openAIModelBodyReplaceFunc) func(bool, string) []byte { + replacedBodies := make(map[string][]byte) + return func(mapped bool, mappedModel string) []byte { + if !mapped { + return body + } + if cachedBody, ok := replacedBodies[mappedModel]; ok { + return cachedBody + } + replacedBody := openAIModelMappedBody(body, true, mappedModel, replace) + replacedBodies[mappedModel] = replacedBody + return replacedBody + } +} + func usageRecordContext(parent context.Context, base context.Context) context.Context { if base == nil { base = context.Background() @@ -241,6 +265,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) { // 解析渠道级模型映射 channelMapping, _ := h.gatewayService.ResolveChannelMappingAndRestrict(c.Request.Context(), apiKey.GroupID, reqModel) + forwardBody := openAIModelMappedBody(body, channelMapping.Mapped, channelMapping.MappedModel, h.gatewayService.ReplaceModelInBody) // 提前校验 function_call_output 是否具备可关联上下文,避免上游 400。 if !h.validateFunctionCallOutputRequest(c, body, reqLog) { @@ -353,11 +378,6 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) { // Forward request service.SetOpsLatencyMs(c, service.OpsRoutingLatencyMsKey, time.Since(routingStart).Milliseconds()) forwardStart := time.Now() - // 应用渠道模型映射到请求体 - forwardBody := body - if channelMapping.Mapped { - forwardBody = h.gatewayService.ReplaceModelInBody(body, channelMapping.MappedModel) - } writerSizeBeforeForward := c.Writer.Size() result, err := func() (*service.OpenAIForwardResult, error) { defer func() { @@ -660,6 +680,7 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) { // 解析渠道级模型映射 channelMappingMsg, _ := h.gatewayService.ResolveChannelMappingAndRestrict(c.Request.Context(), apiKey.GroupID, reqModel) + mappedBodyForMessages := newOpenAIModelMappedBodyCache(body, h.gatewayService.ReplaceModelInBody) // 绑定错误透传服务,允许 service 层在非 failover 错误场景复用规则。 if h.errorPassthroughService != nil { @@ -758,10 +779,7 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) { defaultMappedModel := strings.TrimSpace(effectiveMappedModel) // 应用渠道模型映射到请求体 - forwardBody := body - if channelMappingMsg.Mapped { - forwardBody = h.gatewayService.ReplaceModelInBody(body, channelMappingMsg.MappedModel) - } + forwardBody := mappedBodyForMessages(channelMappingMsg.Mapped, channelMappingMsg.MappedModel) writerSizeBeforeForward := c.Writer.Size() result, err := func() (*service.OpenAIForwardResult, error) { defer func() { @@ -1847,11 +1865,9 @@ func (h *OpenAIGatewayHandler) ensureForwardErrorResponse(c *gin.Context, stream if c == nil || c.Writer == nil { return false } - // 旧实现在 Writer.Written 时直接 return false,导致 ping 已 flush 之后的 - // 上游错误(http2 timeout、连接中断等)完全无法把错误传给客户端—— - // HTTP 200 已锁死,TCP 直接 EOF,Codex CLI 报 "stream closed before response.completed"。 - // 这里改成:Writer 已写过时强制走 streamStarted 分支,让 - // handleStreamingAwareError 通过 SSE 发协议合规的 response.failed。 + if service.IsResponseCommitted(c) { + return false + } if c.Writer.Written() { streamStarted = true } diff --git a/backend/internal/handler/openai_gateway_handler_test.go b/backend/internal/handler/openai_gateway_handler_test.go index e7605e35e8d..7743d6ab623 100644 --- a/backend/internal/handler/openai_gateway_handler_test.go +++ b/backend/internal/handler/openai_gateway_handler_test.go @@ -445,6 +445,41 @@ func TestResolveOpenAIMessagesDispatchMappedModel(t *testing.T) { }) } +func TestOpenAIModelMappedBody(t *testing.T) { + body := []byte(`{"model":"alias","input":"hello"}`) + calls := 0 + + forwardBody := openAIModelMappedBody(body, true, "gpt-5.4", func(body []byte, newModel string) []byte { + calls++ + return service.ReplaceModelInBody(body, newModel) + }) + + require.Equal(t, 1, calls) + require.Equal(t, "gpt-5.4", gjson.GetBytes(forwardBody, "model").String()) + require.Equal(t, "alias", gjson.GetBytes(body, "model").String()) +} + +func TestOpenAIModelMappedBodyCache(t *testing.T) { + body := []byte(`{"model":"alias","input":"hello"}`) + calls := 0 + mappedBody := newOpenAIModelMappedBodyCache(body, func(body []byte, newModel string) []byte { + calls++ + return service.ReplaceModelInBody(body, newModel) + }) + + first := mappedBody(true, "gpt-5.4") + second := mappedBody(true, "gpt-5.4") + third := mappedBody(true, "gpt-5.3-codex") + unmapped := mappedBody(false, "ignored") + + require.Equal(t, 2, calls) + require.Equal(t, "gpt-5.4", gjson.GetBytes(first, "model").String()) + require.Equal(t, "gpt-5.4", gjson.GetBytes(second, "model").String()) + require.Equal(t, "gpt-5.3-codex", gjson.GetBytes(third, "model").String()) + require.Equal(t, body, unmapped) + require.Same(t, &first[0], &second[0]) +} + func TestOpenAIResponses_MissingDependencies_ReturnsServiceUnavailable(t *testing.T) { gin.SetMode(gin.TestMode) diff --git a/backend/internal/handler/page_handler.go b/backend/internal/handler/page_handler.go index 7d4d5078490..a80ae82b1c6 100644 --- a/backend/internal/handler/page_handler.go +++ b/backend/internal/handler/page_handler.go @@ -277,6 +277,7 @@ func RegisterPageRoutes(v1 *gin.RouterGroup, dataDir string, jwtAuth gin.Handler // Admin-only: list all available pages adminPages := v1.Group("/pages") adminPages.Use(adminAuth) + adminPages.Use(middleware2.AdminComplianceGuard(settingService)) { adminPages.GET("", h.ListPages) } diff --git a/backend/internal/handler/wire.go b/backend/internal/handler/wire.go index c8c4615702e..7d7f4dc6225 100644 --- a/backend/internal/handler/wire.go +++ b/backend/internal/handler/wire.go @@ -39,6 +39,7 @@ func ProvideAdminHandlers( contentModerationHandler *admin.ContentModerationHandler, paymentHandler *admin.PaymentHandler, affiliateHandler *admin.AffiliateHandler, + complianceHandler *admin.ComplianceHandler, ) *AdminHandlers { return &AdminHandlers{ Dashboard: dashboardHandler, @@ -71,6 +72,7 @@ func ProvideAdminHandlers( ContentModeration: contentModerationHandler, Payment: paymentHandler, Affiliate: affiliateHandler, + Compliance: complianceHandler, } } @@ -184,6 +186,7 @@ var ProviderSet = wire.NewSet( admin.NewContentModerationHandler, admin.NewPaymentHandler, admin.NewAffiliateHandler, + admin.NewComplianceHandler, // AdminHandlers and Handlers constructors ProvideAdminHandlers, diff --git a/backend/internal/pkg/antigravity/claude_types.go b/backend/internal/pkg/antigravity/claude_types.go index b651db94084..cd68c02788b 100644 --- a/backend/internal/pkg/antigravity/claude_types.go +++ b/backend/internal/pkg/antigravity/claude_types.go @@ -149,6 +149,7 @@ type modelDef struct { // Antigravity 支持的 Claude 模型 var claudeModels = []modelDef{ + {ID: "claude-fable-5", DisplayName: "Claude Fable 5", CreatedAt: "2026-06-09T00:00:00Z"}, {ID: "claude-opus-4-5-thinking", DisplayName: "Claude Opus 4.5 Thinking", CreatedAt: "2025-11-01T00:00:00Z"}, {ID: "claude-sonnet-4-5", DisplayName: "Claude Sonnet 4.5", CreatedAt: "2025-09-29T00:00:00Z"}, {ID: "claude-sonnet-4-5-thinking", DisplayName: "Claude Sonnet 4.5 Thinking", CreatedAt: "2025-09-29T00:00:00Z"}, diff --git a/backend/internal/pkg/antigravity/claude_types_test.go b/backend/internal/pkg/antigravity/claude_types_test.go index fdf5c66de1c..bb45c2f0c37 100644 --- a/backend/internal/pkg/antigravity/claude_types_test.go +++ b/backend/internal/pkg/antigravity/claude_types_test.go @@ -12,6 +12,7 @@ func TestDefaultModels_ContainsNewAndLegacyImageModels(t *testing.T) { } requiredIDs := []string{ + "claude-fable-5", "claude-opus-4-8", "claude-opus-4-6-thinking", "gemini-2.5-flash-image", diff --git a/backend/internal/pkg/antigravity/request_transformer.go b/backend/internal/pkg/antigravity/request_transformer.go index 9068ad97308..ac3610d0ab3 100644 --- a/backend/internal/pkg/antigravity/request_transformer.go +++ b/backend/internal/pkg/antigravity/request_transformer.go @@ -204,6 +204,7 @@ type modelInfo struct { // 只有在此映射表中的模型才会注入身份提示词 // 注意:模型映射逻辑在网关层完成;这里仅用于按模型前缀判断是否注入身份提示词。 var modelInfoMap = map[string]modelInfo{ + "claude-fable-5": {DisplayName: "Claude Fable 5", CanonicalID: "claude-fable-5"}, "claude-opus-4-8": {DisplayName: "Claude Opus 4.8", CanonicalID: "claude-opus-4-8"}, "claude-opus-4-7": {DisplayName: "Claude Opus 4.7", CanonicalID: "claude-opus-4-7"}, "claude-opus-4-5": {DisplayName: "Claude Opus 4.5", CanonicalID: "claude-opus-4-5-20250929"}, diff --git a/backend/internal/pkg/claude/constants.go b/backend/internal/pkg/claude/constants.go index 9529097054e..06335aeda96 100644 --- a/backend/internal/pkg/claude/constants.go +++ b/backend/internal/pkg/claude/constants.go @@ -116,6 +116,12 @@ type Model struct { // DefaultModels Claude Code 客户端支持的默认模型列表 var DefaultModels = []Model{ + { + ID: "claude-fable-5", + Type: "model", + DisplayName: "Claude Fable 5", + CreatedAt: "2026-06-09T00:00:00Z", + }, { ID: "claude-opus-4-5-20251101", Type: "model", diff --git a/backend/internal/repository/user_repo.go b/backend/internal/repository/user_repo.go index 599b9febf49..98122c0bea2 100644 --- a/backend/internal/repository/user_repo.go +++ b/backend/internal/repository/user_repo.go @@ -462,6 +462,17 @@ func (r *userRepository) ListWithFilters(ctx context.Context, params pagination. )) } + if filters.APIKeyGroupID > 0 { + // 按"API Key 实际绑定的分组"过滤:用户只要有任意一个未软删除的 API Key + // 绑定到该分组即命中(EXISTS 语义)。 + // 注意:SoftDeleteMixin 的拦截器不会自动下沉到 HasAPIKeysWith 子查询, + // 必须显式加 apikey.DeletedAtIsNil(),否则已软删除的 key 会污染过滤结果。 + q = q.Where(dbuser.HasAPIKeysWith( + apikey.GroupIDEQ(filters.APIKeyGroupID), + apikey.DeletedAtIsNil(), + )) + } + // If attribute filters are specified, we need to filter by user IDs first var allowedUserIDs []int64 if len(filters.Attributes) > 0 { diff --git a/backend/internal/repository/user_repo_apikey_group_filter_integration_test.go b/backend/internal/repository/user_repo_apikey_group_filter_integration_test.go new file mode 100644 index 00000000000..a23706e33c9 --- /dev/null +++ b/backend/internal/repository/user_repo_apikey_group_filter_integration_test.go @@ -0,0 +1,174 @@ +//go:build integration + +package repository + +import ( + "context" + "testing" + + dbent "github.com/Wei-Shaw/sub2api/ent" + "github.com/Wei-Shaw/sub2api/internal/pkg/pagination" + "github.com/Wei-Shaw/sub2api/internal/service" + "github.com/stretchr/testify/suite" +) + +type UserRepoAPIKeyGroupFilterSuite struct { + suite.Suite + ctx context.Context + client *dbent.Client + repo *userRepository +} + +func (s *UserRepoAPIKeyGroupFilterSuite) SetupTest() { + s.ctx = context.Background() + s.client = testEntClient(s.T()) + s.repo = newUserRepositoryWithSQL(s.client, integrationDB) + // api_keys 必须先于 users 清理(外键);groups 也清理避免跨用例串扰。 + _, _ = integrationDB.ExecContext(s.ctx, "DELETE FROM api_keys") + _, _ = integrationDB.ExecContext(s.ctx, "DELETE FROM user_allowed_groups") + _, _ = integrationDB.ExecContext(s.ctx, "DELETE FROM user_subscriptions") + _, _ = integrationDB.ExecContext(s.ctx, "DELETE FROM users") + _, _ = integrationDB.ExecContext(s.ctx, "DELETE FROM groups") +} + +func TestUserRepoAPIKeyGroupFilterSuite(t *testing.T) { + suite.Run(t, new(UserRepoAPIKeyGroupFilterSuite)) +} + +func (s *UserRepoAPIKeyGroupFilterSuite) mustCreateUser(email string) *service.User { + s.T().Helper() + u := &service.User{ + Email: email, + PasswordHash: "test-password-hash", + Role: service.RoleUser, + Status: service.StatusActive, + Concurrency: 5, + } + s.Require().NoError(s.repo.Create(s.ctx, u), "create user") + return u +} + +func (s *UserRepoAPIKeyGroupFilterSuite) mustCreateGroup(name string) *dbent.Group { + s.T().Helper() + g, err := s.client.Group.Create(). + SetName(name). + SetStatus(service.StatusActive). + Save(s.ctx) + s.Require().NoError(err, "create group") + return g +} + +func (s *UserRepoAPIKeyGroupFilterSuite) mustCreateAPIKey(userID int64, key, name string, groupID *int64) *dbent.APIKey { + s.T().Helper() + create := s.client.APIKey.Create(). + SetUserID(userID). + SetKey(key). + SetName(name) + if groupID != nil { + create = create.SetGroupID(*groupID) + } + ak, err := create.Save(s.ctx) + s.Require().NoError(err, "create api key") + return ak +} + +func (s *UserRepoAPIKeyGroupFilterSuite) ids(users []service.User) []int64 { + out := make([]int64, len(users)) + for i := range users { + out[i] = users[i].ID + } + return out +} + +func (s *UserRepoAPIKeyGroupFilterSuite) listByAPIKeyGroup(groupID int64) []service.User { + s.T().Helper() + users, _, err := s.repo.ListWithFilters( + s.ctx, + pagination.PaginationParams{Page: 1, PageSize: 50}, + service.UserListFilters{APIKeyGroupID: groupID}, + ) + s.Require().NoError(err, "ListWithFilters") + return users +} + +// 命中:拥有绑定到该分组 API Key 的用户出现,绑定到其它分组的不出现。 +func (s *UserRepoAPIKeyGroupFilterSuite) TestFiltersUsersByAPIKeyGroup() { + g := s.mustCreateGroup("grp-target") + other := s.mustCreateGroup("grp-other") + hit := s.mustCreateUser("hit@test.com") + miss := s.mustCreateUser("miss@test.com") + s.mustCreateAPIKey(hit.ID, "sk-hit", "K", &g.ID) + s.mustCreateAPIKey(miss.ID, "sk-miss", "K", &other.ID) + + s.Require().Equal([]int64{hit.ID}, s.ids(s.listByAPIKeyGroup(g.ID))) +} + +// 软删除的 API Key 不应命中(核心:软删除不会自动下沉到子查询,靠 DeletedAtIsNil 排除)。 +func (s *UserRepoAPIKeyGroupFilterSuite) TestSoftDeletedAPIKeyExcluded() { + g := s.mustCreateGroup("grp-soft") + u := s.mustCreateUser("soft@test.com") + ak := s.mustCreateAPIKey(u.ID, "sk-soft", "K", &g.ID) + // 软删除该 key:SoftDeleteMixin 的 Hook 把 Delete 转为 UPDATE deleted_at。 + s.Require().NoError(s.client.APIKey.DeleteOne(ak).Exec(s.ctx), "soft delete api key") + + s.Require().Empty(s.listByAPIKeyGroup(g.ID), "user with only a soft-deleted key must not match") +} + +// 多 Key:用户有多个 key,仅一个绑该分组 → 命中且只返回一条(EXISTS/去重)。 +func (s *UserRepoAPIKeyGroupFilterSuite) TestMultipleKeysAnyMatchDedup() { + g := s.mustCreateGroup("grp-multi") + other := s.mustCreateGroup("grp-multi-other") + u := s.mustCreateUser("multi@test.com") + s.mustCreateAPIKey(u.ID, "sk-m1", "K1", &other.ID) + s.mustCreateAPIKey(u.ID, "sk-m2", "K2", &g.ID) + s.mustCreateAPIKey(u.ID, "sk-m3", "K3", nil) // 无分组 + + s.Require().Equal([]int64{u.ID}, s.ids(s.listByAPIKeyGroup(g.ID))) +} + +// 叠加过滤:api_key_group_id 与 status 同时指定时取交集——只返回同时满足两者的用户。 +func (s *UserRepoAPIKeyGroupFilterSuite) TestAPIKeyGroupAndStatusFilter() { + g := s.mustCreateGroup("grp-combined") + + // active 用户,key 绑 target 分组 → 应命中 + active := s.mustCreateUser("active-hit@test.com") + s.mustCreateAPIKey(active.ID, "sk-active", "K", &g.ID) + + // disabled 用户,key 也绑 target 分组 → 只用 group 过滤会命中,但 status=active 后排除 + disabled := s.mustCreateUser("disabled-hit@test.com") + s.mustCreateAPIKey(disabled.ID, "sk-disabled", "K2", &g.ID) + _, err := s.client.User.UpdateOneID(disabled.ID).SetStatus(service.StatusDisabled).Save(s.ctx) + s.Require().NoError(err, "disable user") + + // active 用户,key 绑其它分组 → group 过滤排除 + other := s.mustCreateGroup("grp-combined-other") + miss := s.mustCreateUser("active-miss@test.com") + s.mustCreateAPIKey(miss.ID, "sk-miss", "K3", &other.ID) + + users, _, err := s.repo.ListWithFilters( + s.ctx, + pagination.PaginationParams{Page: 1, PageSize: 50}, + service.UserListFilters{ + APIKeyGroupID: g.ID, + Status: service.StatusActive, + }, + ) + s.Require().NoError(err) + s.Require().Equal([]int64{active.ID}, s.ids(users), "only active user with matching key group should match") +} + +// 缺省(APIKeyGroupID=0)不过滤:所有用户都返回。 +func (s *UserRepoAPIKeyGroupFilterSuite) TestZeroGroupIDNoFilter() { + g := s.mustCreateGroup("grp-zero") + u1 := s.mustCreateUser("z1@test.com") + u2 := s.mustCreateUser("z2@test.com") + s.mustCreateAPIKey(u1.ID, "sk-z1", "K", &g.ID) + + users, _, err := s.repo.ListWithFilters( + s.ctx, + pagination.PaginationParams{Page: 1, PageSize: 50}, + service.UserListFilters{APIKeyGroupID: 0}, + ) + s.Require().NoError(err) + s.Require().ElementsMatch([]int64{u1.ID, u2.ID}, s.ids(users)) +} diff --git a/backend/internal/server/middleware/admin_compliance.go b/backend/internal/server/middleware/admin_compliance.go new file mode 100644 index 00000000000..39fe633f5ff --- /dev/null +++ b/backend/internal/server/middleware/admin_compliance.go @@ -0,0 +1,53 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/Wei-Shaw/sub2api/internal/service" + + "github.com/gin-gonic/gin" +) + +func AdminComplianceGuard(settingService *service.SettingService) gin.HandlerFunc { + return func(c *gin.Context) { + if settingService == nil || isAdminComplianceBypassPath(c.Request.URL.Path) { + c.Next() + return + } + + subject, ok := GetAuthSubjectFromContext(c) + if !ok { + AbortWithError(c, http.StatusUnauthorized, "UNAUTHORIZED", "Authorization required") + return + } + + acknowledged, err := settingService.IsAdminComplianceAcknowledged(c.Request.Context(), subject.UserID) + if err != nil { + AbortWithError(c, http.StatusInternalServerError, "INTERNAL_ERROR", "Internal server error") + return + } + if acknowledged { + c.Next() + return + } + + c.JSON(http.StatusLocked, gin.H{ + "code": "ADMIN_COMPLIANCE_ACK_REQUIRED", + "message": "administrator compliance acknowledgement is required", + "metadata": gin.H{ + "version": service.AdminComplianceVersion, + "document_path_zh": service.AdminComplianceDocumentPathZH, + "document_path_en": service.AdminComplianceDocumentPathEN, + "document_url_zh": service.AdminComplianceDocumentURLZH, + "document_url_en": service.AdminComplianceDocumentURLEN, + }, + }) + c.Abort() + } +} + +func isAdminComplianceBypassPath(path string) bool { + path = strings.TrimSpace(path) + return path == "/api/v1/admin/compliance" || strings.HasPrefix(path, "/api/v1/admin/compliance/") +} diff --git a/backend/internal/server/middleware/admin_compliance_test.go b/backend/internal/server/middleware/admin_compliance_test.go new file mode 100644 index 00000000000..7c996eb539f --- /dev/null +++ b/backend/internal/server/middleware/admin_compliance_test.go @@ -0,0 +1,82 @@ +package middleware + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Wei-Shaw/sub2api/internal/config" + "github.com/Wei-Shaw/sub2api/internal/service" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/require" +) + +type complianceGuardRepoStub struct { + values map[string]string +} + +func (r *complianceGuardRepoStub) Get(ctx context.Context, key string) (*service.Setting, error) { + if value, ok := r.values[key]; ok { + return &service.Setting{Key: key, Value: value}, nil + } + return nil, service.ErrSettingNotFound +} + +func (r *complianceGuardRepoStub) GetValue(ctx context.Context, key string) (string, error) { + setting, err := r.Get(ctx, key) + if err != nil { + return "", err + } + return setting.Value, nil +} + +func (r *complianceGuardRepoStub) Set(ctx context.Context, key, value string) error { return nil } +func (r *complianceGuardRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) { + return map[string]string{}, nil +} +func (r *complianceGuardRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error { + return nil +} +func (r *complianceGuardRepoStub) GetAll(ctx context.Context) (map[string]string, error) { + return map[string]string{}, nil +} +func (r *complianceGuardRepoStub) Delete(ctx context.Context, key string) error { return nil } + +func TestAdminComplianceGuardBlocksAdminRouteWhenMissing(t *testing.T) { + gin.SetMode(gin.TestMode) + svc := service.NewSettingService(&complianceGuardRepoStub{}, &config.Config{}) + router := gin.New() + router.Use(func(c *gin.Context) { + c.Set(string(ContextKeyUser), AuthSubject{UserID: 1}) + c.Next() + }) + router.Use(AdminComplianceGuard(svc)) + router.GET("/api/v1/admin/users", func(c *gin.Context) { + c.String(http.StatusOK, "ok") + }) + + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/users", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusLocked, w.Code) + require.Contains(t, w.Body.String(), "ADMIN_COMPLIANCE_ACK_REQUIRED") +} + +func TestAdminComplianceGuardBypassesComplianceEndpoint(t *testing.T) { + gin.SetMode(gin.TestMode) + svc := service.NewSettingService(&complianceGuardRepoStub{}, &config.Config{}) + router := gin.New() + router.Use(AdminComplianceGuard(svc)) + router.GET("/api/v1/admin/compliance", func(c *gin.Context) { + c.String(http.StatusOK, "ok") + }) + + req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/compliance", nil) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code) + require.Equal(t, "ok", w.Body.String()) +} diff --git a/backend/internal/server/router.go b/backend/internal/server/router.go index f477f3a754c..3d863737795 100644 --- a/backend/internal/server/router.go +++ b/backend/internal/server/router.go @@ -109,7 +109,7 @@ func registerRoutes( // 注册各模块路由 routes.RegisterAuthRoutes(v1, h, jwtAuth, redisClient, settingService) routes.RegisterUserRoutes(v1, h, jwtAuth, settingService) - routes.RegisterAdminRoutes(v1, h, adminAuth) + routes.RegisterAdminRoutes(v1, h, adminAuth, settingService) routes.RegisterGatewayRoutes(r, h, apiKeyAuth, apiKeyService, subscriptionService, opsService, settingService, cfg) routes.RegisterPaymentRoutes(v1, h.Payment, h.PaymentWebhook, h.Admin.Payment, jwtAuth, adminAuth, settingService) diff --git a/backend/internal/server/routes/admin.go b/backend/internal/server/routes/admin.go index 00452312393..e6afa1436c3 100644 --- a/backend/internal/server/routes/admin.go +++ b/backend/internal/server/routes/admin.go @@ -4,6 +4,7 @@ package routes import ( "github.com/Wei-Shaw/sub2api/internal/handler" "github.com/Wei-Shaw/sub2api/internal/server/middleware" + "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" ) @@ -13,10 +14,15 @@ func RegisterAdminRoutes( v1 *gin.RouterGroup, h *handler.Handlers, adminAuth middleware.AdminAuthMiddleware, + settingService *service.SettingService, ) { admin := v1.Group("/admin") admin.Use(gin.HandlerFunc(adminAuth)) + admin.Use(middleware.AdminComplianceGuard(settingService)) { + // 部署与运营合规确认 + registerAdminComplianceRoutes(admin, h) + // 仪表盘 registerDashboardRoutes(admin, h) @@ -100,6 +106,14 @@ func RegisterAdminRoutes( } } +func registerAdminComplianceRoutes(admin *gin.RouterGroup, h *handler.Handlers) { + compliance := admin.Group("/compliance") + { + compliance.GET("", h.Admin.Compliance.GetStatus) + compliance.POST("/accept", h.Admin.Compliance.Accept) + } +} + func registerContentModerationRoutes(admin *gin.RouterGroup, h *handler.Handlers) { risk := admin.Group("/risk-control") { diff --git a/backend/internal/server/routes/payment.go b/backend/internal/server/routes/payment.go index beeae611463..2e26f2c0a01 100644 --- a/backend/internal/server/routes/payment.go +++ b/backend/internal/server/routes/payment.go @@ -68,6 +68,7 @@ func RegisterPaymentRoutes( // --- Admin payment endpoints (admin auth) --- adminGroup := v1.Group("/admin/payment") adminGroup.Use(gin.HandlerFunc(adminAuth)) + adminGroup.Use(middleware.AdminComplianceGuard(settingService)) { // Dashboard adminGroup.GET("/dashboard", adminPaymentHandler.GetDashboard) diff --git a/backend/internal/service/admin_compliance.go b/backend/internal/service/admin_compliance.go new file mode 100644 index 00000000000..f07e9d9f808 --- /dev/null +++ b/backend/internal/service/admin_compliance.go @@ -0,0 +1,161 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors" +) + +const ( + AdminComplianceVersion = "v2026.06.10" + AdminComplianceDocumentPathZH = "docs/legal/admin-compliance.zh.md" + AdminComplianceDocumentPathEN = "docs/legal/admin-compliance.en.md" + AdminComplianceDocumentURLZH = "https://github.com/Wei-Shaw/sub2api/blob/main/docs/legal/admin-compliance.zh.md" + AdminComplianceDocumentURLEN = "https://github.com/Wei-Shaw/sub2api/blob/main/docs/legal/admin-compliance.en.md" + AdminComplianceAckPhraseZH = "我已阅读、理解并同意 Sub2API 部署与运营合规承诺" + AdminComplianceAckPhraseEN = "I have read, understood, and agree to the Sub2API Deployment and Operation Compliance Commitment" + + settingKeyAdminComplianceAcknowledgement = "admin_compliance_acknowledgement" +) + +var ( + ErrAdminComplianceAcknowledgementRequired = infraerrors.New( + http.StatusLocked, + "ADMIN_COMPLIANCE_ACK_REQUIRED", + "administrator compliance acknowledgement is required", + ) + ErrAdminComplianceInvalidPhrase = infraerrors.BadRequest( + "ADMIN_COMPLIANCE_INVALID_PHRASE", + "confirmation phrase does not match", + ) +) + +type AdminComplianceAcknowledgement struct { + Version string `json:"version"` + DocumentZH string `json:"document_zh"` + DocumentEN string `json:"document_en"` + AdminUserID int64 `json:"admin_user_id"` + IPAddress string `json:"ip_address,omitempty"` + UserAgent string `json:"user_agent,omitempty"` + AcceptedAt time.Time `json:"accepted_at"` +} + +type AdminComplianceStatus struct { + Required bool `json:"required"` + Version string `json:"version"` + DocumentPathZH string `json:"document_path_zh"` + DocumentPathEN string `json:"document_path_en"` + DocumentURLZH string `json:"document_url_zh"` + DocumentURLEN string `json:"document_url_en"` + AckPhraseZH string `json:"ack_phrase_zh"` + AckPhraseEN string `json:"ack_phrase_en"` + Acknowledgement *AdminComplianceAcknowledgement `json:"acknowledgement,omitempty"` +} + +type AdminComplianceAcceptInput struct { + AdminUserID int64 + Phrase string + Language string + IPAddress string + UserAgent string +} + +func normalizeAdminComplianceLanguage(raw string) string { + raw = strings.ToLower(strings.TrimSpace(raw)) + if strings.HasPrefix(raw, "zh") { + return "zh" + } + return "en" +} + +func expectedAdminCompliancePhrase(language string) string { + if normalizeAdminComplianceLanguage(language) == "zh" { + return AdminComplianceAckPhraseZH + } + return AdminComplianceAckPhraseEN +} + +func adminComplianceAcknowledgementKey(adminUserID int64) string { + if adminUserID <= 0 { + return settingKeyAdminComplianceAcknowledgement + } + return settingKeyAdminComplianceAcknowledgement + ":" + strconv.FormatInt(adminUserID, 10) +} + +func (s *SettingService) GetAdminComplianceStatus(ctx context.Context, adminUserID int64) (*AdminComplianceStatus, error) { + status := &AdminComplianceStatus{ + Required: true, + Version: AdminComplianceVersion, + DocumentPathZH: AdminComplianceDocumentPathZH, + DocumentPathEN: AdminComplianceDocumentPathEN, + DocumentURLZH: AdminComplianceDocumentURLZH, + DocumentURLEN: AdminComplianceDocumentURLEN, + AckPhraseZH: AdminComplianceAckPhraseZH, + AckPhraseEN: AdminComplianceAckPhraseEN, + } + if s == nil || s.settingRepo == nil { + return status, nil + } + + raw, err := s.settingRepo.GetValue(ctx, adminComplianceAcknowledgementKey(adminUserID)) + if err != nil { + if errors.Is(err, ErrSettingNotFound) { + return status, nil + } + return nil, fmt.Errorf("get admin compliance acknowledgement: %w", err) + } + + var ack AdminComplianceAcknowledgement + if err := json.Unmarshal([]byte(raw), &ack); err != nil { + return status, nil + } + if ack.Version == AdminComplianceVersion { + status.Required = false + status.Acknowledgement = &ack + } + return status, nil +} + +func (s *SettingService) IsAdminComplianceAcknowledged(ctx context.Context, adminUserID int64) (bool, error) { + status, err := s.GetAdminComplianceStatus(ctx, adminUserID) + if err != nil { + return false, err + } + return status != nil && !status.Required, nil +} + +func (s *SettingService) AcceptAdminCompliance(ctx context.Context, input AdminComplianceAcceptInput) (*AdminComplianceStatus, error) { + if s == nil || s.settingRepo == nil { + return nil, infraerrors.InternalServer("SETTING_SERVICE_UNAVAILABLE", "setting service is unavailable") + } + phrase := strings.TrimSpace(input.Phrase) + if phrase != expectedAdminCompliancePhrase(input.Language) { + return nil, ErrAdminComplianceInvalidPhrase + } + + ack := AdminComplianceAcknowledgement{ + Version: AdminComplianceVersion, + DocumentZH: AdminComplianceDocumentPathZH, + DocumentEN: AdminComplianceDocumentPathEN, + AdminUserID: input.AdminUserID, + IPAddress: strings.TrimSpace(input.IPAddress), + UserAgent: strings.TrimSpace(input.UserAgent), + AcceptedAt: time.Now().UTC(), + } + payload, err := json.Marshal(ack) + if err != nil { + return nil, fmt.Errorf("marshal admin compliance acknowledgement: %w", err) + } + if err := s.settingRepo.Set(ctx, adminComplianceAcknowledgementKey(input.AdminUserID), string(payload)); err != nil { + return nil, fmt.Errorf("save admin compliance acknowledgement: %w", err) + } + + return s.GetAdminComplianceStatus(ctx, input.AdminUserID) +} diff --git a/backend/internal/service/admin_compliance_test.go b/backend/internal/service/admin_compliance_test.go new file mode 100644 index 00000000000..1f1a2c862f9 --- /dev/null +++ b/backend/internal/service/admin_compliance_test.go @@ -0,0 +1,133 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "testing" + + "github.com/Wei-Shaw/sub2api/internal/config" + "github.com/stretchr/testify/require" +) + +type adminComplianceRepoStub struct { + values map[string]string +} + +func (r *adminComplianceRepoStub) Get(ctx context.Context, key string) (*Setting, error) { + if value, ok := r.values[key]; ok { + return &Setting{Key: key, Value: value}, nil + } + return nil, ErrSettingNotFound +} + +func (r *adminComplianceRepoStub) GetValue(ctx context.Context, key string) (string, error) { + setting, err := r.Get(ctx, key) + if err != nil { + return "", err + } + return setting.Value, nil +} + +func (r *adminComplianceRepoStub) Set(ctx context.Context, key, value string) error { + if r.values == nil { + r.values = map[string]string{} + } + r.values[key] = value + return nil +} + +func (r *adminComplianceRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) { + return map[string]string{}, nil +} + +func (r *adminComplianceRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error { + return nil +} + +func (r *adminComplianceRepoStub) GetAll(ctx context.Context) (map[string]string, error) { + return map[string]string{}, nil +} + +func (r *adminComplianceRepoStub) Delete(ctx context.Context, key string) error { + delete(r.values, key) + return nil +} + +func TestAdminComplianceStatusRequiresAckWhenMissing(t *testing.T) { + svc := NewSettingService(&adminComplianceRepoStub{}, &config.Config{}) + + status, err := svc.GetAdminComplianceStatus(context.Background(), 1) + require.NoError(t, err) + require.True(t, status.Required) + require.Equal(t, AdminComplianceVersion, status.Version) + require.Equal(t, AdminComplianceAckPhraseZH, status.AckPhraseZH) + require.Equal(t, AdminComplianceDocumentPathZH, status.DocumentPathZH) +} + +func TestAcceptAdminComplianceRejectsWrongPhrase(t *testing.T) { + svc := NewSettingService(&adminComplianceRepoStub{}, &config.Config{}) + + _, err := svc.AcceptAdminCompliance(context.Background(), AdminComplianceAcceptInput{ + AdminUserID: 1, + Language: "zh", + Phrase: "我同意", + }) + require.Error(t, err) + require.True(t, errors.Is(err, ErrAdminComplianceInvalidPhrase)) +} + +func TestAcceptAdminCompliancePersistsCurrentVersion(t *testing.T) { + repo := &adminComplianceRepoStub{} + svc := NewSettingService(repo, &config.Config{}) + + status, err := svc.AcceptAdminCompliance(context.Background(), AdminComplianceAcceptInput{ + AdminUserID: 42, + Language: "zh-CN", + Phrase: AdminComplianceAckPhraseZH, + IPAddress: "203.0.113.10", + UserAgent: "test-agent", + }) + require.NoError(t, err) + require.False(t, status.Required) + require.NotNil(t, status.Acknowledgement) + require.Equal(t, int64(42), status.Acknowledgement.AdminUserID) + require.Equal(t, "203.0.113.10", status.Acknowledgement.IPAddress) + + var stored AdminComplianceAcknowledgement + require.NoError(t, json.Unmarshal([]byte(repo.values[adminComplianceAcknowledgementKey(42)]), &stored)) + require.Equal(t, AdminComplianceVersion, stored.Version) + require.Equal(t, AdminComplianceDocumentPathZH, stored.DocumentZH) +} + +func TestAdminComplianceStatusRequiresAckOnOldVersion(t *testing.T) { + old, err := json.Marshal(AdminComplianceAcknowledgement{Version: "v2026.01.01"}) + require.NoError(t, err) + svc := NewSettingService(&adminComplianceRepoStub{ + values: map[string]string{adminComplianceAcknowledgementKey(1): string(old)}, + }, &config.Config{}) + + status, err := svc.GetAdminComplianceStatus(context.Background(), 1) + require.NoError(t, err) + require.True(t, status.Required) + require.Nil(t, status.Acknowledgement) +} + +func TestAdminComplianceStatusIsPerAdminUser(t *testing.T) { + current, err := json.Marshal(AdminComplianceAcknowledgement{ + Version: AdminComplianceVersion, + AdminUserID: 1, + }) + require.NoError(t, err) + svc := NewSettingService(&adminComplianceRepoStub{ + values: map[string]string{adminComplianceAcknowledgementKey(1): string(current)}, + }, &config.Config{}) + + statusForUserOne, err := svc.GetAdminComplianceStatus(context.Background(), 1) + require.NoError(t, err) + require.False(t, statusForUserOne.Required) + + statusForUserTwo, err := svc.GetAdminComplianceStatus(context.Background(), 2) + require.NoError(t, err) + require.True(t, statusForUserTwo.Required) +} diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index c945c843ad3..5a7c14f00a3 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -52,6 +52,9 @@ type AdminService interface { ListGroups(ctx context.Context, page, pageSize int, platform, status, search string, isExclusive *bool, sortBy, sortOrder string) ([]Group, int64, error) GetAllGroups(ctx context.Context) ([]Group, error) GetAllGroupsByPlatform(ctx context.Context, platform string) ([]Group, error) + // GetAllGroupsIncludingInactive returns all groups regardless of status (active + disabled), + // ordered by sort_order then id. Used by the API Key group filter dropdown. + GetAllGroupsIncludingInactive(ctx context.Context) ([]Group, error) GetGroup(ctx context.Context, id int64) (*Group, error) GetGroupModelsListCandidates(ctx context.Context, id int64, platform string) ([]string, error) CreateGroup(ctx context.Context, input *CreateGroupInput) (*Group, error) @@ -1701,6 +1704,13 @@ func (s *adminServiceImpl) GetAllGroupsByPlatform(ctx context.Context, platform return s.groupRepo.ListActiveByPlatform(ctx, platform) } +func (s *adminServiceImpl) GetAllGroupsIncludingInactive(ctx context.Context) ([]Group, error) { + // ListWithFilters with empty status = no status filter, so active + disabled groups are returned. + // PageSize 10000 is intentionally large; group count is O(dozens) in practice. + groups, _, err := s.groupRepo.ListWithFilters(ctx, pagination.PaginationParams{Page: 1, PageSize: 10000}, "", "", "", nil) + return groups, err +} + func (s *adminServiceImpl) GetGroup(ctx context.Context, id int64) (*Group, error) { return s.groupRepo.GetByID(ctx, id) } diff --git a/backend/internal/service/antigravity_gateway_service.go b/backend/internal/service/antigravity_gateway_service.go index f79d20a2ca9..7bdda2e5079 100644 --- a/backend/internal/service/antigravity_gateway_service.go +++ b/backend/internal/service/antigravity_gateway_service.go @@ -2447,6 +2447,7 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co Detail: upstreamDetail, }) logger.LegacyPrintf("service.antigravity_gateway", "[antigravity-Forward] upstream error status=%d body=%s", resp.StatusCode, truncateForLog(unwrappedForOps, 500)) + MarkResponseCommitted(c) c.Data(resp.StatusCode, contentType, unwrappedForOps) return nil, fmt.Errorf("antigravity upstream error: %d", resp.StatusCode) } @@ -3644,6 +3645,7 @@ func mergeTextPartsToResponse(response map[string]any, textParts []string) map[s } func (s *AntigravityGatewayService) writeClaudeError(c *gin.Context, status int, errType, message string) error { + MarkResponseCommitted(c) c.JSON(status, gin.H{ "type": "error", "error": gin.H{"type": errType, "message": message}, @@ -3657,6 +3659,7 @@ func (s *AntigravityGatewayService) WriteMappedClaudeError(c *gin.Context, accou } func (s *AntigravityGatewayService) writeMappedClaudeError(c *gin.Context, account *Account, upstreamStatus int, upstreamRequestID string, body []byte) error { + MarkResponseCommitted(c) upstreamMsg := strings.TrimSpace(extractUpstreamErrorMessage(body)) upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg) logBody, maxBytes := s.getLogConfig() @@ -3734,6 +3737,7 @@ func (s *AntigravityGatewayService) writeMappedClaudeError(c *gin.Context, accou } func (s *AntigravityGatewayService) writeGoogleError(c *gin.Context, status int, message string) error { + MarkResponseCommitted(c) statusStr := "UNKNOWN" switch status { case 400: diff --git a/backend/internal/service/antigravity_model_mapping_test.go b/backend/internal/service/antigravity_model_mapping_test.go index 652b6d66992..62ff2e295d5 100644 --- a/backend/internal/service/antigravity_model_mapping_test.go +++ b/backend/internal/service/antigravity_model_mapping_test.go @@ -76,6 +76,12 @@ func TestAntigravityGatewayService_GetMappedModel(t *testing.T) { }, // 3. 默认映射中的透传(映射到自己) + { + name: "默认映射透传 - claude-fable-5", + requestedModel: "claude-fable-5", + accountMapping: nil, + expected: "claude-fable-5", + }, { name: "默认映射透传 - claude-sonnet-4-6", requestedModel: "claude-sonnet-4-6", @@ -218,6 +224,7 @@ func TestAntigravityGatewayService_IsModelSupported(t *testing.T) { expected bool }{ // 直接支持 + {"直接支持 - claude-fable-5", "claude-fable-5", true}, {"直接支持 - claude-sonnet-4-5", "claude-sonnet-4-5", true}, {"直接支持 - gemini-3-flash", "gemini-3-flash", true}, diff --git a/backend/internal/service/bedrock_request.go b/backend/internal/service/bedrock_request.go index f4416ce3a69..595f687ac1d 100644 --- a/backend/internal/service/bedrock_request.go +++ b/backend/internal/service/bedrock_request.go @@ -215,8 +215,14 @@ func PrepareBedrockRequestBodyWithTokens(body []byte, modelID string, betaTokens return nil, fmt.Errorf("inject anthropic_beta: %w", err) } logger.LegacyPrintf("service.gateway", "[Bedrock] Injected beta tokens: %v (model=%s ccCompat=%v)", betaTokens, modelID, ccCompat) + } else { + body, _ = sjson.DeleteBytes(body, "anthropic_beta") } + // 移除 Bedrock 不支持的 Anthropic 直连 API 专有顶层字段 + body, _ = sjson.DeleteBytes(body, "provider") + body, _ = sjson.DeleteBytes(body, "metadata") + // 移除 model 字段(Bedrock 通过 URL 指定模型) body, err = sjson.DeleteBytes(body, "model") if err != nil { @@ -348,6 +354,9 @@ var claudeVersionRe = regexp.MustCompile(`claude-(?:haiku|sonnet|opus)-(\d+)[-.] // Claude 4.5+ 支持 cache_control 中的 ttl 字段("5m" 和 "1h") func isBedrockClaude45OrNewer(modelID string) bool { lower := strings.ToLower(modelID) + if isBedrockFable5(lower) { + return true + } matches := claudeVersionRe.FindStringSubmatch(lower) if matches == nil { return false @@ -465,11 +474,12 @@ func parseAnthropicBetaHeader(header string) []string { // 参考: AWS Bedrock 官方文档 + litellm anthropic_beta_headers_config.json // 更新策略: 当 AWS Bedrock 新增支持的 beta token 时需同步更新此白名单 var bedrockSupportedBetaTokens = map[string]bool{ - "computer-use-2025-01-24": true, - "computer-use-2025-11-24": true, - "context-1m-2025-08-07": true, - // "context-management-2025-06-27": false, // 无官方文档支持 - "compact-2026-01-12": true, // 官方支持,仅 InvokeModel API(Opus 4.6+) + "computer-use-2025-01-24": true, + "computer-use-2025-11-24": true, + "context-1m-2025-08-07": true, + "context-management-2025-06-27": true, // compaction + clear_thinking,AWS 文档已支持 + "compact-2026-01-12": true, // 官方支持,仅 InvokeModel API(Opus 4.6+) + "fine-grained-tool-streaming-2025-05-14": true, // AWS Tool Use 文档已支持 // "interleaved-thinking-2025-05-14": false, // 无官方文档支持 "tool-search-tool-2025-10-19": true, "tool-examples-2025-10-29": true, @@ -658,9 +668,14 @@ func isBedrockOpus47OrNewer(modelID string) bool { return major > 4 || (major == 4 && minor >= 7) } +func isBedrockFable5(modelID string) bool { + return strings.Contains(strings.ToLower(modelID), "claude-fable-5") +} + const defaultThinkingBudgetTokens = 10000 // sanitizeBedrockThinking 修复 thinking 字段的 Bedrock 兼容性问题: +// - Fable 5: 仅使用 always-on adaptive thinking,不支持手动 budget_tokens // - Opus 4.7+: 仅支持 "adaptive",将 "enabled" 转换为 "adaptive" 并移除 budget_tokens // - 其他模型: "enabled" 必须带 budget_tokens,缺失时补充默认值 func sanitizeBedrockThinking(body []byte, modelID string) []byte { @@ -674,6 +689,16 @@ func sanitizeBedrockThinking(body []byte, modelID string) []byte { return body } + if isBedrockFable5(modelID) { + if thinkingType == "enabled" { + body, _ = sjson.SetBytes(body, "thinking.type", "adaptive") + } + if thinkingType == "enabled" || thinkingType == "adaptive" { + body, _ = sjson.DeleteBytes(body, "thinking.budget_tokens") + } + return body + } + if isBedrockOpus47OrNewer(modelID) { if thinkingType == "enabled" { body, _ = sjson.SetBytes(body, "thinking.type", "adaptive") diff --git a/backend/internal/service/bedrock_request_test.go b/backend/internal/service/bedrock_request_test.go index c5b71da74f0..d580f8c1ba5 100644 --- a/backend/internal/service/bedrock_request_test.go +++ b/backend/internal/service/bedrock_request_test.go @@ -175,6 +175,7 @@ func TestIsBedrockClaude45OrNewer(t *testing.T) { }{ {"us.anthropic.claude-opus-4-6-v1", true}, {"us.anthropic.claude-opus-4-8-v1", true}, + {"anthropic.claude-fable-5", true}, {"us.anthropic.claude-sonnet-4-6", true}, {"us.anthropic.claude-sonnet-4-5-20250929-v1:0", true}, {"us.anthropic.claude-opus-4-5-20251101-v1:0", true}, @@ -411,7 +412,7 @@ func TestPrepareBedrockRequestBodyWithTokens_ContextManagementRequiresSupportedB assert.Equal(t, int64(100), gjson.GetBytes(result, "max_tokens").Int()) }) - t.Run("filters explicit unsupported context-management beta and strips field", func(t *testing.T) { + t.Run("keeps supported context-management beta and retains field", func(t *testing.T) { input := `{ "messages":[{"role":"user","content":"hi"}], "max_tokens":100, @@ -426,8 +427,8 @@ func TestPrepareBedrockRequestBodyWithTokens_ContextManagementRequiresSupportedB ) require.NoError(t, err) - assert.False(t, gjson.GetBytes(result, "context_management").Exists()) - assert.Equal(t, []string{"context-1m-2025-08-07"}, bedrockAnthropicBetaNames(result)) + assert.True(t, gjson.GetBytes(result, "context_management").Exists()) + assert.Equal(t, []string{bedrockContextManagementBetaToken, "context-1m-2025-08-07"}, bedrockAnthropicBetaNames(result)) }) } @@ -526,6 +527,20 @@ func TestResolveBedrockModelID(t *testing.T) { assert.Equal(t, "eu.anthropic.claude-opus-4-8-v1", modelID) }) + t.Run("默认 Fable 5 映射使用官方 Bedrock 模型 ID", func(t *testing.T) { + account := &Account{ + Platform: PlatformAnthropic, + Type: AccountTypeBedrock, + Credentials: map[string]any{ + "aws_region": "eu-west-1", + }, + } + + modelID, ok := ResolveBedrockModelID(account, "claude-fable-5") + require.True(t, ok) + assert.Equal(t, "anthropic.claude-fable-5", modelID) + }) + t.Run("force global rewrites anthropic regional model id", func(t *testing.T) { account := &Account{ Platform: PlatformAnthropic, @@ -750,6 +765,20 @@ func TestIsBedrockOpus47OrNewer(t *testing.T) { } func TestSanitizeBedrockThinking(t *testing.T) { + t.Run("Fable 5 将 enabled 转换为 adaptive 并移除预算", func(t *testing.T) { + input := `{"thinking":{"type":"enabled","budget_tokens":10000},"messages":[]}` + result := sanitizeBedrockThinking([]byte(input), "anthropic.claude-fable-5") + assert.Equal(t, "adaptive", gjson.GetBytes(result, "thinking.type").String()) + assert.False(t, gjson.GetBytes(result, "thinking.budget_tokens").Exists()) + }) + + t.Run("Fable 5 adaptive 移除预算", func(t *testing.T) { + input := `{"thinking":{"type":"adaptive","budget_tokens":10000},"messages":[]}` + result := sanitizeBedrockThinking([]byte(input), "claude-fable-5") + assert.Equal(t, "adaptive", gjson.GetBytes(result, "thinking.type").String()) + assert.False(t, gjson.GetBytes(result, "thinking.budget_tokens").Exists()) + }) + t.Run("opus 4.7 converts enabled to adaptive", func(t *testing.T) { input := `{"thinking":{"type":"enabled","budget_tokens":10000},"messages":[]}` result := sanitizeBedrockThinking([]byte(input), "us.anthropic.claude-opus-4-7-v1") diff --git a/backend/internal/service/error_passthrough_runtime_test.go b/backend/internal/service/error_passthrough_runtime_test.go index 7032d15b950..73a4bfab194 100644 --- a/backend/internal/service/error_passthrough_runtime_test.go +++ b/backend/internal/service/error_passthrough_runtime_test.go @@ -251,6 +251,90 @@ func TestApplyErrorPassthroughRule_NoSkipMonitoringDoesNotSetContextKey(t *testi assert.False(t, exists, "OpsSkipPassthroughKey should NOT be set when skip_monitoring=false") } +// ---- ResponseCommittedKey: service 层写完错误响应后标记,handler 层检查跳过兜底写入 ---- + +func TestHandleErrorResponse_SetsResponseCommitted(t *testing.T) { + gin.SetMode(gin.TestMode) + rec := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rec) + + svc := &GatewayService{} + resp := &http.Response{ + StatusCode: http.StatusBadRequest, + Body: io.NopCloser(bytes.NewReader([]byte(`{"error":{"message":"temperature: range: 0..1"}}`))), + Header: http.Header{}, + } + account := &Account{ID: 100, Platform: PlatformAnthropic, Type: AccountTypeAPIKey} + + _, err := svc.handleErrorResponse(context.Background(), resp, c, account) + require.Error(t, err) + assert.True(t, IsResponseCommitted(c), "non-failover error path must mark response committed") + var payload map[string]any + require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload)) +} + +func TestHandleErrorResponse_PassthroughRuleSetsCommitted(t *testing.T) { + gin.SetMode(gin.TestMode) + rec := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rec) + + ruleSvc := &ErrorPassthroughService{} + ruleSvc.setLocalCache([]*model.ErrorPassthroughRule{ + newNonFailoverPassthroughRule(http.StatusBadRequest, "temperature", http.StatusBadRequest, "参数错误"), + }) + BindErrorPassthroughService(c, ruleSvc) + + svc := &GatewayService{} + resp := &http.Response{ + StatusCode: http.StatusBadRequest, + Body: io.NopCloser(bytes.NewReader([]byte(`{"error":{"message":"temperature: range: 0..1"}}`))), + Header: http.Header{}, + } + account := &Account{ID: 200, Platform: PlatformAnthropic, Type: AccountTypeAPIKey} + + _, err := svc.handleErrorResponse(context.Background(), resp, c, account) + require.Error(t, err) + assert.True(t, IsResponseCommitted(c), "passthrough rule path must mark response committed") + assert.Equal(t, http.StatusBadRequest, rec.Code) + var payload map[string]any + require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload)) + errField, ok := payload["error"].(map[string]any) + require.True(t, ok, "payload[\"error\"] should be map[string]any") + assert.Equal(t, "参数错误", errField["message"]) +} + +func TestOpenAIHandleErrorResponse_SetsResponseCommitted(t *testing.T) { + gin.SetMode(gin.TestMode) + rec := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rec) + + svc := &OpenAIGatewayService{} + resp := &http.Response{ + StatusCode: http.StatusTooManyRequests, + Body: io.NopCloser(bytes.NewReader([]byte(`{"error":{"message":"rate limit exceeded"}}`))), + Header: http.Header{}, + } + account := &Account{ID: 101, Platform: PlatformOpenAI, Type: AccountTypeAPIKey} + + _, err := svc.handleErrorResponse(context.Background(), resp, c, account, nil) + require.Error(t, err) + assert.True(t, IsResponseCommitted(c), "OpenAI non-failover path must mark response committed") +} + +func TestGeminiWriteGeminiMappedError_SetsResponseCommitted(t *testing.T) { + gin.SetMode(gin.TestMode) + rec := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rec) + + svc := &GeminiMessagesCompatService{} + body := []byte(`{"error":{"message":"invalid field"}}`) + account := &Account{ID: 102, Platform: PlatformGemini, Type: AccountTypeAPIKey} + + err := svc.writeGeminiMappedError(c, account, http.StatusBadRequest, "req-99", body) + require.Error(t, err) + assert.True(t, IsResponseCommitted(c), "Gemini path must mark response committed") +} + func newNonFailoverPassthroughRule(statusCode int, keyword string, respCode int, customMessage string) *model.ErrorPassthroughRule { return &model.ErrorPassthroughRule{ ID: 1, diff --git a/backend/internal/service/gateway_forward_as_chat_completions.go b/backend/internal/service/gateway_forward_as_chat_completions.go index 16c10a32986..67324d63ba4 100644 --- a/backend/internal/service/gateway_forward_as_chat_completions.go +++ b/backend/internal/service/gateway_forward_as_chat_completions.go @@ -498,6 +498,7 @@ func (s *GatewayService) handleCCStreamingFromAnthropic( // writeGatewayCCError writes an error in OpenAI Chat Completions format for // the Anthropic-upstream CC forwarding path. func writeGatewayCCError(c *gin.Context, statusCode int, errType, message string) { + MarkResponseCommitted(c) c.JSON(statusCode, gin.H{ "error": gin.H{ "type": errType, diff --git a/backend/internal/service/gateway_forward_as_responses.go b/backend/internal/service/gateway_forward_as_responses.go index f883ae870f0..83adce6cad0 100644 --- a/backend/internal/service/gateway_forward_as_responses.go +++ b/backend/internal/service/gateway_forward_as_responses.go @@ -520,6 +520,7 @@ func appendRawJSON(existing json.RawMessage, fragment string) json.RawMessage { // writeResponsesError writes an error response in OpenAI Responses API format. func writeResponsesError(c *gin.Context, statusCode int, code, message string) { + MarkResponseCommitted(c) c.JSON(statusCode, gin.H{ "error": gin.H{ "code": code, diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go index 531c93e4e75..82b57f30412 100644 --- a/backend/internal/service/gateway_service.go +++ b/backend/internal/service/gateway_service.go @@ -2361,14 +2361,16 @@ func (s *GatewayService) listSchedulableAccounts(ctx context.Context, groupID *i "platform", platform, "use_mixed", useMixed, "count", len(accounts)) - for _, acc := range accounts { - slog.Debug("account_scheduling_account_detail", - "account_id", acc.ID, - "name", acc.Name, - "platform", acc.Platform, - "type", acc.Type, - "status", acc.Status, - "tls_fingerprint", acc.IsTLSFingerprintEnabled()) + if slog.Default().Enabled(ctx, slog.LevelDebug) { + for _, acc := range accounts { + slog.Debug("account_scheduling_account_detail", + "account_id", acc.ID, + "name", acc.Name, + "platform", acc.Platform, + "type", acc.Type, + "status", acc.Status, + "tls_fingerprint", acc.IsTLSFingerprintEnabled()) + } } } return accounts, useMixed, err @@ -2404,14 +2406,16 @@ func (s *GatewayService) listSchedulableAccounts(ctx context.Context, groupID *i "platform", platform, "raw_count", len(accounts), "filtered_count", len(filtered)) - for _, acc := range filtered { - slog.Debug("account_scheduling_account_detail", - "account_id", acc.ID, - "name", acc.Name, - "platform", acc.Platform, - "type", acc.Type, - "status", acc.Status, - "tls_fingerprint", acc.IsTLSFingerprintEnabled()) + if slog.Default().Enabled(ctx, slog.LevelDebug) { + for _, acc := range filtered { + slog.Debug("account_scheduling_account_detail", + "account_id", acc.ID, + "name", acc.Name, + "platform", acc.Platform, + "type", acc.Type, + "status", acc.Status, + "tls_fingerprint", acc.IsTLSFingerprintEnabled()) + } } return filtered, useMixed, nil } @@ -2437,14 +2441,16 @@ func (s *GatewayService) listSchedulableAccounts(ctx context.Context, groupID *i "group_id", derefGroupID(groupID), "platform", platform, "count", len(accounts)) - for _, acc := range accounts { - slog.Debug("account_scheduling_account_detail", - "account_id", acc.ID, - "name", acc.Name, - "platform", acc.Platform, - "type", acc.Type, - "status", acc.Status, - "tls_fingerprint", acc.IsTLSFingerprintEnabled()) + if slog.Default().Enabled(ctx, slog.LevelDebug) { + for _, acc := range accounts { + slog.Debug("account_scheduling_account_detail", + "account_id", acc.ID, + "name", acc.Name, + "platform", acc.Platform, + "type", acc.Type, + "status", acc.Status, + "tls_fingerprint", acc.IsTLSFingerprintEnabled()) + } } return accounts, useMixed, nil } @@ -5860,15 +5866,24 @@ func writeAnthropicPassthroughResponseHeaders(dst http.Header, src http.Header, } // ApplyBedrockCCCompat 应用 Bedrock CC 兼容转换(渠道级模型映射后调用) -// 清理 Anthropic API 专有字段、注入 Bedrock 必需字段、修复 thinking/tool_use ID -func (s *GatewayService) ApplyBedrockCCCompat(ctx context.Context, body []byte, model string, account *Account, groupID *int64) []byte { - if !s.isBedrockCCCompatEnabled(ctx, account, groupID) { +// 清理 body 中 Anthropic API 专有字段、修复 thinking/tool_use ID、过滤 beta token, +// 同时过滤 HTTP header 中的 anthropic-beta(防止 Passthrough 路径透传不支持的 token)。 +func (s *GatewayService) ApplyBedrockCCCompat(c *gin.Context, body []byte, model string, account *Account, groupID *int64) []byte { + if !s.isBedrockCCCompatEnabled(c.Request.Context(), account, groupID) { return body } body = sanitizeBedrockCCFields(body) body = sanitizeBedrockThinking(body, model) body = sanitizeBedrockToolUseIDs(body) body = sanitizeBedrockCCBetaTokens(body, model) + // 过滤 HTTP header 中的 anthropic-beta,只保留 Bedrock 支持的 token + if betaHeader := c.GetHeader("anthropic-beta"); betaHeader != "" { + if filtered := ResolveBedrockBetaTokens(betaHeader, body, model); len(filtered) > 0 { + c.Request.Header.Set("anthropic-beta", strings.Join(filtered, ", ")) + } else { + c.Request.Header.Del("anthropic-beta") + } + } return body } @@ -7353,6 +7368,8 @@ func (s *GatewayService) handleErrorResponse(ctx context.Context, resp *http.Res return nil, &UpstreamFailoverError{StatusCode: resp.StatusCode, ResponseBody: body} } + MarkResponseCommitted(c) + // 记录上游错误响应体摘要便于排障(可选:由配置控制;不回显到客户端) if s.cfg != nil && s.cfg.Gateway.LogUpstreamErrorBody { logger.LegacyPrintf("service.gateway", @@ -7476,6 +7493,7 @@ func (s *GatewayService) handleFailoverSideEffects(ctx context.Context, resp *ht // OAuth 403:标记账号异常 // API Key 未配置错误码:仅返回错误,不标记账号 func (s *GatewayService) handleRetryExhaustedError(ctx context.Context, resp *http.Response, c *gin.Context, account *Account) (*ForwardResult, error) { + MarkResponseCommitted(c) // Capture upstream error body before side-effects consume the stream. respBody, _ := s.readUpstreamErrorBody(resp) _ = resp.Body.Close() diff --git a/backend/internal/service/gemini_messages_compat_service.go b/backend/internal/service/gemini_messages_compat_service.go index 86073d9ca54..375fb05326e 100644 --- a/backend/internal/service/gemini_messages_compat_service.go +++ b/backend/internal/service/gemini_messages_compat_service.go @@ -1449,6 +1449,7 @@ func (s *GeminiMessagesCompatService) ForwardNative(ctx context.Context, c *gin. if contentType == "" { contentType = "application/json" } + MarkResponseCommitted(c) c.Data(http.StatusInternalServerError, contentType, respBody) return nil, fmt.Errorf("gemini upstream error: %d (skipped by error policy)", resp.StatusCode) case ErrorPolicyMatched, ErrorPolicyTempUnscheduled: @@ -1561,6 +1562,7 @@ func (s *GeminiMessagesCompatService) ForwardNative(ctx context.Context, c *gin. if contentType == "" { contentType = "application/json" } + MarkResponseCommitted(c) c.Data(resp.StatusCode, contentType, respBody) if upstreamMsg == "" { return nil, fmt.Errorf("gemini upstream error: %d", resp.StatusCode) @@ -1700,6 +1702,7 @@ func sanitizeUpstreamErrorMessage(msg string) string { } func (s *GeminiMessagesCompatService) writeGeminiMappedError(c *gin.Context, account *Account, upstreamStatus int, upstreamRequestID string, body []byte) error { + MarkResponseCommitted(c) upstreamMsg := strings.TrimSpace(extractUpstreamErrorMessage(body)) upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg) upstreamDetail := "" @@ -2243,6 +2246,7 @@ func randomHex(nBytes int) string { } func (s *GeminiMessagesCompatService) writeClaudeError(c *gin.Context, status int, errType, message string) error { + MarkResponseCommitted(c) c.JSON(status, gin.H{ "type": "error", "error": gin.H{"type": errType, "message": message}, @@ -2251,6 +2255,7 @@ func (s *GeminiMessagesCompatService) writeClaudeError(c *gin.Context, status in } func (s *GeminiMessagesCompatService) writeGoogleError(c *gin.Context, status int, message string) error { + MarkResponseCommitted(c) c.JSON(status, gin.H{ "error": gin.H{ "code": status, diff --git a/backend/internal/service/idempotency.go b/backend/internal/service/idempotency.go index 2a86bd60386..42414687895 100644 --- a/backend/internal/service/idempotency.go +++ b/backend/internal/service/idempotency.go @@ -11,6 +11,7 @@ import ( "strings" "sync" "time" + "unicode/utf8" infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors" "github.com/Wei-Shaw/sub2api/internal/util/logredact" @@ -454,11 +455,21 @@ func (c *IdempotencyCoordinator) marshalStoredResponse(data any) (string, error) } redacted := logredact.RedactText(string(raw)) if c.cfg.MaxStoredResponseLen > 0 && len(redacted) > c.cfg.MaxStoredResponseLen { - redacted = redacted[:c.cfg.MaxStoredResponseLen] + "...(truncated)" + redacted = truncateUTF8(redacted, c.cfg.MaxStoredResponseLen) + "...(truncated)" } return redacted, nil } +func truncateUTF8(s string, maxBytes int) string { + if maxBytes <= 0 || len(s) <= maxBytes { + return s + } + for maxBytes > 0 && !utf8.ValidString(s[:maxBytes]) { + maxBytes-- + } + return s[:maxBytes] +} + func (c *IdempotencyCoordinator) decodeStoredResponse(stored *string) (any, error) { if stored == nil || strings.TrimSpace(*stored) == "" { return map[string]any{}, nil diff --git a/backend/internal/service/idempotency_test.go b/backend/internal/service/idempotency_test.go index 6ff75d1c3e7..e11c9e75f42 100644 --- a/backend/internal/service/idempotency_test.go +++ b/backend/internal/service/idempotency_test.go @@ -3,10 +3,12 @@ package service import ( "context" "errors" + "strings" "sync" "sync/atomic" "testing" "time" + "unicode/utf8" infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors" "github.com/stretchr/testify/require" @@ -441,6 +443,51 @@ func TestIdempotencyCoordinator_StoreUnavailableMetrics(t *testing.T) { require.GreaterOrEqual(t, GetIdempotencyMetricsSnapshot().StoreUnavailableTotal, uint64(1)) } +type utf8RejectingIdempotencyRepo struct { + inMemoryIdempotencyRepo +} + +func newUTF8RejectingIdempotencyRepo() *utf8RejectingIdempotencyRepo { + return &utf8RejectingIdempotencyRepo{inMemoryIdempotencyRepo: *newInMemoryIdempotencyRepo()} +} + +func (r *utf8RejectingIdempotencyRepo) MarkSucceeded(ctx context.Context, id int64, responseStatus int, responseBody string, expiresAt time.Time) error { + if !utf8.ValidString(responseBody) { + return errors.New(`pq: invalid byte sequence for encoding "UTF8": 0xe8 0xb4 0x2e`) + } + return r.inMemoryIdempotencyRepo.MarkSucceeded(ctx, id, responseStatus, responseBody, expiresAt) +} + +func TestIdempotencyCoordinator_TruncatedStoredResponseRemainsUTF8(t *testing.T) { + repo := newUTF8RejectingIdempotencyRepo() + cfg := DefaultIdempotencyConfig() + cfg.MaxStoredResponseLen = len(`{"message":"`) + 2 + coordinator := NewIdempotencyCoordinator(repo, cfg) + + opts := IdempotencyExecuteOptions{ + Scope: "test.scope.truncate_utf8", + Method: "POST", + Route: "/api/v1/accounts/import/codex-session", + ActorScope: "admin:1", + RequireKey: true, + IdempotencyKey: "truncate-utf8", + Payload: map[string]any{"content": "codex-session"}, + } + + result, err := coordinator.Execute(context.Background(), opts, func(ctx context.Context) (any, error) { + return map[string]any{"message": strings.Repeat("\u8d26", 8)}, nil + }) + require.NoError(t, err) + require.NotNil(t, result) + + stored, err := repo.GetByScopeAndKeyHash(context.Background(), opts.Scope, HashIdempotencyKey(opts.IdempotencyKey)) + require.NoError(t, err) + require.NotNil(t, stored) + require.NotNil(t, stored.ResponseBody) + require.True(t, utf8.ValidString(*stored.ResponseBody)) + require.Contains(t, *stored.ResponseBody, "...(truncated)") +} + func TestDefaultIdempotencyCoordinatorAndTTLs(t *testing.T) { SetDefaultIdempotencyCoordinator(nil) require.Nil(t, DefaultIdempotencyCoordinator()) diff --git a/backend/internal/service/openai_gateway_chat_completions.go b/backend/internal/service/openai_gateway_chat_completions.go index ddc717f2f74..8b75097349f 100644 --- a/backend/internal/service/openai_gateway_chat_completions.go +++ b/backend/internal/service/openai_gateway_chat_completions.go @@ -187,6 +187,22 @@ func (s *OpenAIGatewayService) ForwardAsChatCompletions( } } + if account.Type == AccountTypeAPIKey { + if trimmedKey := strings.TrimSpace(promptCacheKey); trimmedKey != "" { + var reqBody map[string]any + if err := json.Unmarshal(responsesBody, &reqBody); err != nil { + return nil, fmt.Errorf("unmarshal for prompt cache key injection: %w", err) + } + if existing, ok := reqBody["prompt_cache_key"].(string); !ok || strings.TrimSpace(existing) == "" { + reqBody["prompt_cache_key"] = trimmedKey + responsesBody, err = json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("remarshal after prompt cache key injection: %w", err) + } + } + } + } + // 4b. Apply OpenAI fast policy (may filter service_tier or block the request). updatedBody, policyErr := s.applyOpenAIFastPolicyToBody(ctx, account, upstreamModel, responsesBody) if policyErr != nil { @@ -214,7 +230,8 @@ func (s *OpenAIGatewayService) ForwardAsChatCompletions( } if promptCacheKey != "" { - upstreamReq.Header.Set("session_id", generateSessionUUID(promptCacheKey)) + apiKeyID := getAPIKeyIDFromContext(c) + upstreamReq.Header.Set("session_id", generateSessionUUID(isolateOpenAISessionID(apiKeyID, promptCacheKey))) } // 7. Send request @@ -836,6 +853,7 @@ func (s *OpenAIGatewayService) handleChatStreamingResponse( // writeChatCompletionsError writes an error response in OpenAI Chat Completions format. func writeChatCompletionsError(c *gin.Context, statusCode int, errType, message string) { + MarkResponseCommitted(c) c.JSON(statusCode, gin.H{ "error": gin.H{ "type": errType, diff --git a/backend/internal/service/openai_gateway_chat_completions_test.go b/backend/internal/service/openai_gateway_chat_completions_test.go index 58aea1d716a..779565037dc 100644 --- a/backend/internal/service/openai_gateway_chat_completions_test.go +++ b/backend/internal/service/openai_gateway_chat_completions_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/Wei-Shaw/sub2api/internal/config" "github.com/Wei-Shaw/sub2api/internal/pkg/apicompat" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" @@ -112,7 +113,10 @@ func TestForwardAsChatCompletions_UnknownModelDoesNotUseDefaultMappedModel(t *te Body: io.NopCloser(strings.NewReader(`{"error":{"type":"invalid_request_error","message":"model not found"}}`)), }} - svc := &OpenAIGatewayService{httpUpstream: upstream} + svc := &OpenAIGatewayService{ + cfg: &config.Config{}, + httpUpstream: upstream, + } account := &Account{ ID: 1, Name: "openai-oauth", @@ -133,6 +137,50 @@ func TestForwardAsChatCompletions_UnknownModelDoesNotUseDefaultMappedModel(t *te require.Equal(t, http.StatusBadRequest, rec.Code) } +func TestForwardAsChatCompletions_APIKeyPropagatesPromptCacheKeyInResponsesBody(t *testing.T) { + gin.SetMode(gin.TestMode) + + rec := httptest.NewRecorder() + c, _ := gin.CreateTestContext(rec) + body := []byte(`{"model":"gpt-5.4","messages":[{"role":"user","content":"hello"}],"stream":false}`) + c.Request = httptest.NewRequest(http.MethodPost, "/v1/chat/completions", bytes.NewReader(body)) + c.Request.Header.Set("Content-Type", "application/json") + c.Set("api_key", &APIKey{ID: 99}) + + upstream := &httpUpstreamRecorder{resp: &http.Response{ + StatusCode: http.StatusBadRequest, + Header: http.Header{"Content-Type": []string{"application/json"}, "x-request-id": []string{"rid_chat_prompt_cache"}}, + Body: io.NopCloser(strings.NewReader(`{"error":{"type":"invalid_request_error","message":"stop before response parsing"}}`)), + }} + + svc := &OpenAIGatewayService{ + cfg: &config.Config{}, + httpUpstream: upstream, + } + account := &Account{ + ID: 2, + Name: "openai-compatible", + Platform: PlatformOpenAI, + Type: AccountTypeAPIKey, + Concurrency: 1, + Credentials: map[string]any{ + "api_key": "sk-compatible", + }, + Extra: map[string]any{ + "openai_responses_supported": true, + }, + } + + result, err := svc.ForwardAsChatCompletions(context.Background(), c, account, body, "cache-key-123", "gpt-5.4") + require.Error(t, err) + require.Nil(t, result) + require.Equal(t, "cache-key-123", gjson.GetBytes(upstream.lastBody, "prompt_cache_key").String()) + require.Equal(t, "gpt-5.4", gjson.GetBytes(upstream.lastBody, "model").String()) + require.Equal(t, "https://api.openai.com/v1/responses", upstream.lastReq.URL.String()) + require.Equal(t, "Bearer sk-compatible", upstream.lastReq.Header.Get("Authorization")) + require.Equal(t, generateSessionUUID(isolateOpenAISessionID(99, "cache-key-123")), upstream.lastReq.Header.Get("session_id")) +} + func TestForwardAsChatCompletions_ClientDisconnectDrainsUpstreamUsage(t *testing.T) { gin.SetMode(gin.TestMode) diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index be4dc539ff0..6374ee91bdd 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -3552,6 +3552,7 @@ func (s *OpenAIGatewayService) handleErrorResponsePassthrough( account *Account, requestBody []byte, ) error { + MarkResponseCommitted(c) body := s.readUpstreamErrorBody(resp) upstreamMsg := strings.TrimSpace(extractUpstreamErrorMessage(body)) @@ -4289,6 +4290,7 @@ func (s *OpenAIGatewayService) handleErrorResponse( "upstream_error", "Upstream request failed", ); matched { + MarkResponseCommitted(c) c.JSON(status, gin.H{ "error": gin.H{ "type": errType, @@ -4316,6 +4318,7 @@ func (s *OpenAIGatewayService) handleErrorResponse( Message: upstreamMsg, Detail: upstreamDetail, }) + MarkResponseCommitted(c) c.JSON(http.StatusInternalServerError, gin.H{ "error": gin.H{ "type": "upstream_error", @@ -4359,6 +4362,8 @@ func (s *OpenAIGatewayService) handleErrorResponse( } } + MarkResponseCommitted(c) + // Return appropriate error response var errType, errMsg string var statusCode int @@ -4438,6 +4443,7 @@ func (s *OpenAIGatewayService) handleCompatErrorResponse( c, account.Platform, resp.StatusCode, body, http.StatusBadGateway, "api_error", "Upstream request failed", ); matched { + MarkResponseCommitted(c) writeError(c, status, errType, errMsg) if upstreamMsg == "" { upstreamMsg = errMsg @@ -4461,6 +4467,7 @@ func (s *OpenAIGatewayService) handleCompatErrorResponse( Message: upstreamMsg, Detail: upstreamDetail, }) + MarkResponseCommitted(c) writeError(c, http.StatusInternalServerError, "api_error", "Upstream gateway error") if upstreamMsg == "" { return nil, fmt.Errorf("upstream error: %d (not in custom error codes)", resp.StatusCode) @@ -4498,6 +4505,8 @@ func (s *OpenAIGatewayService) handleCompatErrorResponse( } } + MarkResponseCommitted(c) + // Map status code to error type and write response errType := "api_error" switch { diff --git a/backend/internal/service/ops_upstream_context.go b/backend/internal/service/ops_upstream_context.go index 2405f306fee..4a1e36aa6ff 100644 --- a/backend/internal/service/ops_upstream_context.go +++ b/backend/internal/service/ops_upstream_context.go @@ -34,6 +34,10 @@ const ( // Client-side configuration denials should remain visible in ops_error_logs, // but should be excluded from SLA/error-rate calculations. + // ResponseCommittedKey 由 handleErrorResponse 系列函数在写完 HTTP 错误响应后设置。 + // ensureForwardErrorResponse 检查此 key,为 true 时跳过兜底写入,避免在已完成的 JSON 后追加 SSE。 + ResponseCommittedKey = "response_committed" + OpsClientBusinessLimitedKey = "ops_client_business_limited" OpsClientBusinessLimitedReasonKey = "ops_client_business_limited_reason" OpsClientBusinessLimitedReasonIPRestriction = "api_key_ip_restriction" @@ -43,6 +47,17 @@ const ( OpsClientBusinessLimitedReasonLocalPolicyDenied = "local_policy_denied" ) +func MarkResponseCommitted(c *gin.Context) { c.Set(ResponseCommittedKey, true) } + +func IsResponseCommitted(c *gin.Context) bool { + v, ok := c.Get(ResponseCommittedKey) + if !ok { + return false + } + b, _ := v.(bool) + return b +} + func SetOpsLatencyMs(c *gin.Context, key string, value int64) { if c == nil || strings.TrimSpace(key) == "" || value < 0 { return diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index f801e2c4cb6..2c872214016 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -65,11 +65,15 @@ var ( // UserListFilters contains all filter options for listing users type UserListFilters struct { - Status string // User status filter - Role string // User role filter - Search string // Search in email, username - GroupName string // Filter by allowed group name (fuzzy match) - Attributes map[int64]string // Custom attribute filters: attributeID -> value + Status string // User status filter + Role string // User role filter + Search string // Search in email, username + GroupName string // Filter by allowed group name (fuzzy match) + // APIKeyGroupID filters users who own at least one non-soft-deleted API key + // bound to this group (api_keys.group_id). 0 = no filter. Covers all three + // group types since it matches the key's group directly, not allowed_groups. + APIKeyGroupID int64 + Attributes map[int64]string // Custom attribute filters: attributeID -> value // IncludeSubscriptions controls whether ListWithFilters should load active subscriptions. // For large datasets this can be expensive; admin list pages should enable it on demand. // nil means not specified (default: load subscriptions for backward compatibility). diff --git a/backend/migrations/150_account_group_scheduler_indexes_notx.sql b/backend/migrations/150_account_group_scheduler_indexes_notx.sql new file mode 100644 index 00000000000..9fe2344fca9 --- /dev/null +++ b/backend/migrations/150_account_group_scheduler_indexes_notx.sql @@ -0,0 +1,5 @@ +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_groups_group_priority_account + ON account_groups (group_id, priority, account_id); + +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_groups_account_priority_group + ON account_groups (account_id, priority, group_id); diff --git a/deploy/DOCKER.md b/deploy/DOCKER.md index 156b6c97a94..81943fffd93 100644 --- a/deploy/DOCKER.md +++ b/deploy/DOCKER.md @@ -74,3 +74,21 @@ volumes: - [GitHub Repository](https://github.com/weishaw/sub2api) - [Documentation](https://github.com/weishaw/sub2api#readme) + +本地 docker 运行 + +cd deploy + +export http_proxy=http://127.0.0.1:7890 +export https_proxy=http://127.0.0.1:7890 +export HTTP_PROXY=http://127.0.0.1:7890 +export HTTPS_PROXY=http://127.0.0.1:7890 +docker compose -f docker-compose.dev.yml up --build -d + +上传自己的 docker + +export http_proxy=http://127.0.0.1:7890 +export https_proxy=http://127.0.0.1:7890 +export HTTP_PROXY=http://127.0.0.1:7890 +export HTTPS_PROXY=http://127.0.0.1:7890 +docker buildx build --platform linux/amd64 -t docker.io/doctor11ma/sub2api:latest -t docker.io/doctor11ma/sub2api:v0.1.136 --push . diff --git a/deploy/Dockerfile b/deploy/Dockerfile index d39dd17d5cb..25ae4ad4724 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -46,7 +46,8 @@ ENV GOPROXY=${GOPROXY} ENV GOSUMDB=${GOSUMDB} # Install build dependencies -RUN apk add --no-cache git ca-certificates tzdata +RUN sed -i 's|https://dl-cdn.alpinelinux.org|http://dl-cdn.alpinelinux.org|g' /etc/apk/repositories && \ + apk add --no-cache git ca-certificates tzdata WORKDIR /app/backend @@ -73,12 +74,13 @@ RUN CGO_ENABLED=0 GOOS=linux go build \ FROM ${ALPINE_IMAGE} # Labels -LABEL maintainer="Wei-Shaw " -LABEL description="Sub2API - AI API Gateway Platform" -LABEL org.opencontainers.image.source="https://github.com/Wei-Shaw/sub2api" +LABEL maintainer="look2eye" +LABEL description="Look2eye - AI API Gateway Platform" +LABEL org.opencontainers.image.source="https://github.com/MxyEI/sub2api" # Install runtime dependencies -RUN apk add --no-cache \ +RUN sed -i 's|https://dl-cdn.alpinelinux.org|http://dl-cdn.alpinelinux.org|g' /etc/apk/repositories && \ + apk add --no-cache \ ca-certificates \ tzdata \ curl \ diff --git a/deploy/docker-compose.dev.yml b/deploy/docker-compose.dev.yml index 47e0bcad0c9..241aef1599d 100644 --- a/deploy/docker-compose.dev.yml +++ b/deploy/docker-compose.dev.yml @@ -13,6 +13,10 @@ services: build: context: .. dockerfile: Dockerfile + args: + - HTTP_PROXY=http://host.docker.internal:7890 + - HTTPS_PROXY=http://host.docker.internal:7890 + - NO_PROXY=localhost,127.0.0.1,.local container_name: sub2api-dev restart: unless-stopped ports: diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index fd682a87e66..7f1f32b6b81 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -16,7 +16,7 @@ services: # Sub2API Application # =========================================================================== sub2api: - image: weishaw/sub2api:latest + image: doctor11ma/sub2api:latest container_name: sub2api restart: unless-stopped ulimits: diff --git a/docs/legal/admin-compliance.en.md b/docs/legal/admin-compliance.en.md new file mode 100644 index 00000000000..b03f1f08b4a --- /dev/null +++ b/docs/legal/admin-compliance.en.md @@ -0,0 +1,49 @@ +# Sub2API Deployment and Operation Compliance Commitment + +Version: v2026.06.10 + +This document applies to any individual, organization, or authorized representative that deploys, configures, manages, operates, or effectively controls a Sub2API instance. Before continuing to access or use console functions, the acknowledging party must read, understand, and accept this document in full. + +## 1. Scope + +Sub2API is open-source software. Any self-hosted deployment, modification, hosted operation, external service, commercial use, user management, content processing, data processing, payment settlement, customer support, or upstream account/API usage based on Sub2API is the sole responsibility of the party that deploys, operates, or controls the relevant instance. + +This document does not replace the open-source license, upstream terms of service, user agreements, privacy policies, data processing agreements, commercial contracts, regulatory filings, administrative permits, security assessments, or any other documents, procedures, or obligations required by applicable law or contract. + +## 2. Responsibility of the Deploying or Operating Party + +The acknowledging party must independently assess and continuously comply with the laws, regulations, regulatory requirements, industry rules, contractual obligations, and platform policies that may apply in its location, server location, target-user location, place of actual business operation, and the locations of upstream service providers. + +The acknowledging party must ensure that it has all authorizations, qualifications, filings, permits, assessments, contracts, risk-control capabilities, content-safety capabilities, data-protection capabilities, complaint-handling mechanisms, and emergency-response capabilities required for deploying and operating the relevant instance. Such obligations are not transferred, waived, or reduced by the use of open-source software. + +## 3. No Affiliation and Allocation of Responsibility + +Any third-party instance, commercial service, paid plan, user solicitation, content processing, data processing, account usage, API call, payment settlement, customer support, or promotional activity is independently carried out by the corresponding deploying, operating, or controlling party. The open-source nature of this project, code contributions, issue discussions, documentation maintenance, version releases, bug fixes, community communications, or general technical explanations do not create participation in, authorization of, approval of, warranty for, joint operation, agency, partnership, employment, authorized operation, joint control, revenue sharing, joint tort, or any other joint-and-several liability relationship between the open-source project, copyright holders, contributors, or maintainers and such activities. + +The acknowledging party must not use the project name, marks, documentation, screenshots, community content, or open-source repository information to state or imply that its third-party instance, commercial service, paid plan, or operation is participated in, authorized, approved, warranted, or endorsed by the open-source project, copyright holders, contributors, maintainers, or community. + +The acknowledging party is independently responsible for consequences arising from its deployment, configuration, operation, promotion, charging, user-behavior management, content processing, data processing, account usage, API calls, or violations of laws, regulations, regulatory requirements, contractual obligations, or upstream rules. + +Any mandatory liability that cannot be excluded or limited by agreement shall be handled according to applicable law. Such statutory exception does not constitute participation in, authorization of, approval of, warranty for, or endorsement of any third-party deployment, operation, or commercial activity. + +## 4. Compliance Commitments + +By continuing to use console functions, the acknowledging party makes the following commitments: + +1. It has independently reviewed and will continuously comply with the terms of service, acceptable use policies, supported countries and regions, account/API key rules, commercial-use requirements, resale restrictions, risk-control requirements, and technical restrictions of OpenAI, Anthropic, Google, and any other upstream service providers. +2. It will not use this project to bypass, or assist others in bypassing, upstream regional restrictions, access restrictions, account restrictions, risk controls, billing restrictions, identity verification, usage limits, or terms of service. +3. It will not provide API relay, model-call resale, account quota distribution, shared subscriptions, paid calls, top-up/payment agency, or similar services to the public or an indefinite group of users unless all necessary authorizations, qualifications, filings, permits, assessments, or contractual arrangements have been obtained. +4. If it provides generative AI services, deep synthesis services, algorithm-related services, API relay, paid calls, or other potentially regulated services within Mainland China or to the Mainland China public, it will independently complete all potentially applicable obligations regarding internet information services, generative AI services, deep synthesis, algorithm filing, security assessment, cybersecurity, data security, personal information protection, content safety, payment settlement, taxes, and upstream authorization. +5. It will maintain user management, access control, content review, abuse handling, log retention, privacy protection, data deletion, complaint handling, emergency takedown, and security incident response mechanisms appropriate to the scale and risk of its business. +6. It will not make any statement, commitment, marketing representation, or warranty to any user, customer, partner, channel, regulator, or third party that conflicts with Section 3 of this document. +7. It will be independently responsible for consequences arising from its deployment, operation, promotion, charging, user-behavior management, content processing, data processing, account usage, API calls, or violations of laws, regulations, regulatory requirements, contractual obligations, or upstream rules. + +## 5. Risk and Responsibility Notice + +Using Sub2API for public API services, commercial relay, quota distribution, team sharing, paid calls, or similar purposes may involve risks relating to terms of service, contractual breach, data protection, content safety, consumer protection, payment settlement, taxes, export controls, sanctions compliance, cybersecurity, industry access, and administrative regulation. Requirements vary by jurisdiction and business model and may change over time. + +The mandatory notice, document link, exact-phrase acknowledgment, and local acknowledgment record in the console are intended to provide clear, conspicuous, and reproducible notice of deployment and operation risks, confirm that the console user has read the current version of this document, and create a clear responsibility-separation record between the open-source project, copyright holders, contributors, maintainers and any third-party deploying, operating, or controlling party. + +## 6. Electronic Acknowledgment + +By continuing to use the console, opening the document link, reading this document, and typing the required confirmation phrase exactly as displayed, the acknowledging party electronically confirms that it has read, understood, and agreed to this document, and agrees that the system may record necessary evidence including the acknowledged version, acknowledgment time, console account identifier, IP address, and User-Agent. diff --git a/docs/legal/admin-compliance.zh.md b/docs/legal/admin-compliance.zh.md new file mode 100644 index 00000000000..9e0c580de9a --- /dev/null +++ b/docs/legal/admin-compliance.zh.md @@ -0,0 +1,49 @@ +# Sub2API 部署与运营合规承诺 + +版本:v2026.06.10 + +本文件适用于部署、配置、管理、运营或实际控制 Sub2API 实例的个人、组织及其授权代表。继续访问或使用控制台功能前,确认主体应完整阅读、理解并接受本文件。 + +## 一、适用范围 + +Sub2API 是开源软件。任何基于 Sub2API 进行的自部署、二次开发、托管运行、对外服务、商业化使用、用户管理、内容处理、数据处理、支付结算、客户支持及上游账号或接口使用行为,均由相应实例的部署、运营或控制主体自行负责。 + +本文件不替代开源许可证、上游服务条款、用户协议、隐私政策、数据处理协议、商业合同、监管备案、行政许可、安全评估或其他依法应当具备的文件、手续或义务。 + +## 二、主体责任 + +确认主体应自行评估并持续遵守其所在地、服务器所在地、目标用户所在地、业务实际开展地以及上游服务提供方所在地可能适用的法律法规、监管要求、行业规范、合同约定和平台规则。 + +确认主体应确保其已具备部署和运营相关实例所需的授权、资质、备案、许可、评估、合同、风控能力、内容安全能力、数据保护能力、投诉处理机制和应急处置能力。相关义务不得因使用开源软件而转移、免除或降低。 + +## 三、非关联关系与责任隔离 + +任何第三方实例、商业服务、收费套餐、用户招揽、内容处理、数据处理、账号使用、接口调用、支付结算、客户支持或推广活动,均由相应部署、运营或控制主体独立实施,并不因本项目开源、代码贡献、议题讨论、文档维护、版本发布、缺陷修复、社区交流或一般性技术说明而形成开源项目、著作权人、贡献者或维护者对该等活动的参与、授权、认可、担保、共同经营、代理、合伙、雇佣、授权运营、共同控制、收益分配、共同侵权或其他连带责任关系。 + +确认主体不得以项目名称、标识、文档、截图、社区内容或开源仓库信息明示或暗示其第三方实例、商业服务、收费套餐或运营活动获得开源项目、著作权人、贡献者、维护者或社区的参与、授权、认可、担保或背书。 + +确认主体应独立承担因其部署、配置、运营、推广、收费、用户行为管理、内容处理、数据处理、账号使用、接口调用及违反法律法规、监管要求、合同约定或上游规则所产生的相关后果。 + +依法不得由协议排除或限制的强制性责任,依相关法律规定处理;该等法定例外不构成对任何第三方部署、运营或商业活动的参与、授权、认可、担保或背书。 + +## 四、合规承诺 + +确认主体在继续使用控制台功能时,作出以下承诺: + +1. 已独立审阅并将持续遵守 OpenAI、Anthropic、Google 及其他上游服务提供方的服务条款、可接受使用政策、支持国家和地区、账号/API Key 使用规则、商业使用要求、转售限制、风控要求和技术限制。 +2. 不利用本项目规避或协助他人规避上游服务的地区限制、访问限制、账号限制、风控限制、计费限制、身份验证、使用限制或服务条款。 +3. 不在缺乏必要授权、资质、备案、许可、评估或合同安排的情况下,向公众或不特定对象提供 API 中转、模型调用转售、账号额度分发、共享订阅、付费调用、代充代付或其他类似服务。 +4. 如在中国大陆境内或面向中国大陆公众提供生成式人工智能服务、深度合成服务、算法相关服务、API 中转、付费调用或其他可能受监管服务,将自行完成可能适用的互联网信息服务、生成式人工智能服务、深度合成、算法备案、安全评估、网络安全、数据安全、个人信息保护、内容安全、支付结算、税务及上游授权等义务。 +5. 建立与业务规模和风险相匹配的用户管理、访问控制、内容审核、滥用处理、日志留存、隐私保护、数据删除、投诉处理、应急下线和安全事件响应机制。 +6. 不向任何用户、客户、合作方、渠道方、监管机构或第三方作出与本文件第三条相冲突的陈述、承诺、宣传或保证。 +7. 对其部署、运营、推广、收费、用户行为管理、内容处理、数据处理、账号使用、接口调用及违反法律法规、监管要求、合同约定或上游规则所产生的后果独立承担责任。 + +## 五、风险与责任提示 + +将 Sub2API 用于公开 API 服务、商业中转、额度分发、团队共享、付费调用或类似用途,可能涉及服务条款、合同违约、数据保护、内容安全、消费者权益、支付结算、税务、出口管制、制裁合规、网络安全、行业准入及行政监管等风险。不同司法辖区和业务场景的要求可能不同,并可能随时间变化。 + +控制台中的强制提示、协议链接、逐字输入确认和本地确认记录,旨在以清晰、显著、可留痕的方式提示部署与运营风险,确认控制台使用者已阅读当前版本文件,并在开源项目、著作权人、贡献者、维护者与第三方部署、运营或控制主体之间形成明确的责任隔离记录。 + +## 六、电子确认 + +确认主体通过继续使用控制台、打开协议链接、阅读本文件并按页面要求逐字输入确认短语,即表示其以电子方式确认已阅读、理解并同意本文件,并同意系统记录确认版本、确认时间、控制台账户标识、IP 地址和 User-Agent 等必要留痕信息。 diff --git a/frontend/package.json b/frontend/package.json index fd39ba340d0..1a4fb951041 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -57,10 +57,5 @@ "vite-plugin-checker": "^0.9.1", "vitest": "^2.1.9", "vue-tsc": "^2.2.0" - }, - "pnpm": { - "overrides": { - "js-cookie": "3.0.7" - } } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml deleted file mode 100644 index e23fd3f63b3..00000000000 --- a/frontend/pnpm-lock.yaml +++ /dev/null @@ -1,9675 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - js-cookie: 3.0.7 - -importers: - - .: - dependencies: - '@airwallex/components-sdk': - specifier: ^1.30.2 - version: 1.30.2 - '@lobehub/icons': - specifier: ^4.0.2 - version: 4.0.2(@lobehub/ui@4.9.2)(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@stripe/stripe-js': - specifier: ^9.0.1 - version: 9.0.1 - '@tanstack/vue-virtual': - specifier: ^3.13.23 - version: 3.13.23(vue@3.5.26(typescript@5.6.3)) - '@vueuse/core': - specifier: ^10.7.0 - version: 10.11.1(vue@3.5.26(typescript@5.6.3)) - axios: - specifier: ^1.16.0 - version: 1.16.0 - chart.js: - specifier: ^4.4.1 - version: 4.5.1 - dompurify: - specifier: ^3.3.1 - version: 3.3.1 - driver.js: - specifier: ^1.4.0 - version: 1.4.0 - file-saver: - specifier: ^2.0.5 - version: 2.0.5 - marked: - specifier: ^17.0.1 - version: 17.0.1 - pinia: - specifier: ^2.1.7 - version: 2.3.1(typescript@5.6.3)(vue@3.5.26(typescript@5.6.3)) - qrcode: - specifier: ^1.5.4 - version: 1.5.4 - vue: - specifier: ^3.4.0 - version: 3.5.26(typescript@5.6.3) - vue-chartjs: - specifier: ^5.3.0 - version: 5.3.3(chart.js@4.5.1)(vue@3.5.26(typescript@5.6.3)) - vue-draggable-plus: - specifier: ^0.6.1 - version: 0.6.1(@types/sortablejs@1.15.9) - vue-i18n: - specifier: ^9.14.5 - version: 9.14.5(vue@3.5.26(typescript@5.6.3)) - vue-router: - specifier: ^4.2.5 - version: 4.6.4(vue@3.5.26(typescript@5.6.3)) - xlsx: - specifier: ^0.18.5 - version: 0.18.5 - devDependencies: - '@types/dompurify': - specifier: ^3.0.5 - version: 3.2.0 - '@types/file-saver': - specifier: ^2.0.7 - version: 2.0.7 - '@types/mdx': - specifier: ^2.0.13 - version: 2.0.13 - '@types/node': - specifier: ^20.10.5 - version: 20.19.27 - '@types/qrcode': - specifier: ^1.5.6 - version: 1.5.6 - '@typescript-eslint/eslint-plugin': - specifier: ^7.18.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) - '@typescript-eslint/parser': - specifier: ^7.18.0 - version: 7.18.0(eslint@8.57.1)(typescript@5.6.3) - '@vitejs/plugin-vue': - specifier: ^5.2.3 - version: 5.2.4(vite@5.4.21(@types/node@20.19.27))(vue@3.5.26(typescript@5.6.3)) - '@vitest/coverage-v8': - specifier: ^2.1.9 - version: 2.1.9(vitest@2.1.9(@types/node@20.19.27)(jsdom@24.1.3)) - '@vue/test-utils': - specifier: ^2.4.6 - version: 2.4.6 - autoprefixer: - specifier: ^10.4.16 - version: 10.4.23(postcss@8.5.6) - eslint: - specifier: ^8.57.0 - version: 8.57.1 - eslint-plugin-vue: - specifier: ^9.25.0 - version: 9.33.0(eslint@8.57.1) - jsdom: - specifier: ^24.1.3 - version: 24.1.3 - postcss: - specifier: ^8.4.32 - version: 8.5.6 - tailwindcss: - specifier: ^3.4.0 - version: 3.4.19 - typescript: - specifier: ~5.6.0 - version: 5.6.3 - vite: - specifier: ^5.0.10 - version: 5.4.21(@types/node@20.19.27) - vite-plugin-checker: - specifier: ^0.9.1 - version: 0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.6.3)(vite@5.4.21(@types/node@20.19.27))(vue-tsc@2.2.12(typescript@5.6.3)) - vitest: - specifier: ^2.1.9 - version: 2.1.9(@types/node@20.19.27)(jsdom@24.1.3) - vue-tsc: - specifier: ^2.2.0 - version: 2.2.12(typescript@5.6.3) - -packages: - - '@airwallex/airtracker@3.2.0': - resolution: {integrity: sha512-PKE5N38ajTVg6ph9JzLpWsICNjqLtf/wWudNVU3UPX9SVy2I5s5ITc281sMSD8+LIE6RJoGjGTO+VYP/io5kig==} - - '@airwallex/components-sdk@1.30.2': - resolution: {integrity: sha512-BGwAPCACwOJm8XNxDxJGMq1o/73D9+ZWifvp5YHvfgIwxg1RGVCIME0tP1g8cash3fVLHgl7xObyS1QbIOSDXw==} - - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@ant-design/colors@8.0.1': - resolution: {integrity: sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==} - - '@ant-design/cssinjs-utils@2.1.2': - resolution: {integrity: sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==} - peerDependencies: - react: '>=18' - react-dom: '>=18' - - '@ant-design/cssinjs@2.0.1': - resolution: {integrity: sha512-Lw1Z4cUQxdMmTNir67gU0HCpTl5TtkKCJPZ6UBvCqzcOTl/QmMFB6qAEoj8qFl0CuZDX9qQYa3m9+rEKfaBSbA==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/cssinjs@2.1.2': - resolution: {integrity: sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/fast-color@3.0.1': - resolution: {integrity: sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==} - engines: {node: '>=8.x'} - - '@ant-design/icons-svg@4.4.2': - resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} - - '@ant-design/icons@6.1.1': - resolution: {integrity: sha512-AMT4N2y++TZETNHiM77fs4a0uPVCJGuL5MTonk13Pvv7UN7sID1cNEZOc1qNqx6zLKAOilTEFAdAoAFKa0U//Q==} - engines: {node: '>=8'} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/react-slick@2.0.0': - resolution: {integrity: sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==} - peerDependencies: - react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - - '@asamuzakjp/css-color@3.2.0': - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} - engines: {node: '>=6.9.0'} - - '@base-ui/react@1.3.0': - resolution: {integrity: sha512-FwpKqZbPz14AITp1CVgf4AjhKPe1OeeVKSBMdgD10zbFlj3QSWelmtCMLi2+/PFZZcIm3l87G7rwtCZJwHyXWA==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true - - '@base-ui/utils@0.2.6': - resolution: {integrity: sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw==} - peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true - - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - '@braintree/sanitize-url@7.1.2': - resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} - - '@chevrotain/cst-dts-gen@12.0.0': - resolution: {integrity: sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==} - - '@chevrotain/gast@12.0.0': - resolution: {integrity: sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==} - - '@chevrotain/regexp-to-ast@12.0.0': - resolution: {integrity: sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==} - - '@chevrotain/types@12.0.0': - resolution: {integrity: sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==} - - '@chevrotain/utils@12.0.0': - resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==} - - '@csstools/color-helpers@5.1.0': - resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} - engines: {node: '>=18'} - - '@csstools/css-calc@2.1.4': - resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} - engines: {node: '>=18'} - peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-color-parser@3.1.0': - resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} - engines: {node: '>=18'} - peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-parser-algorithms@3.0.5': - resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} - engines: {node: '>=18'} - peerDependencies: - '@csstools/css-tokenizer': ^3.0.4 - - '@csstools/css-tokenizer@3.0.4': - resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} - engines: {node: '>=18'} - - '@dnd-kit/accessibility@3.1.1': - resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} - peerDependencies: - react: '>=16.8.0' - - '@dnd-kit/core@6.3.1': - resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@dnd-kit/modifiers@9.0.0': - resolution: {integrity: sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==} - peerDependencies: - '@dnd-kit/core': ^6.3.0 - react: '>=16.8.0' - - '@dnd-kit/sortable@10.0.0': - resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} - peerDependencies: - '@dnd-kit/core': ^6.3.0 - react: '>=16.8.0' - - '@dnd-kit/utilities@3.2.2': - resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} - peerDependencies: - react: '>=16.8.0' - - '@emoji-mart/data@1.2.1': - resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} - - '@emoji-mart/react@1.1.1': - resolution: {integrity: sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==} - peerDependencies: - emoji-mart: ^5.2 - react: ^16.8 || ^17 || ^18 - - '@emotion/babel-plugin@11.13.5': - resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} - - '@emotion/cache@11.14.0': - resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} - - '@emotion/css@11.13.5': - resolution: {integrity: sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==} - - '@emotion/hash@0.8.0': - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} - - '@emotion/hash@0.9.2': - resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - - '@emotion/is-prop-valid@1.4.0': - resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} - - '@emotion/memoize@0.9.0': - resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} - - '@emotion/react@11.14.0': - resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} - peerDependencies: - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - - '@emotion/serialize@1.3.3': - resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} - - '@emotion/sheet@1.4.0': - resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} - - '@emotion/unitless@0.10.0': - resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} - - '@emotion/unitless@0.7.5': - resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - - '@emotion/use-insertion-effect-with-fallbacks@1.2.0': - resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} - peerDependencies: - react: '>=16.8.0' - - '@emotion/utils@1.4.2': - resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} - - '@emotion/weak-memoize@0.4.0': - resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} - - '@floating-ui/dom@1.7.6': - resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} - - '@floating-ui/react-dom@2.1.8': - resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/react@0.27.19': - resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} - peerDependencies: - react: '>=17.0.0' - react-dom: '>=17.0.0' - - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - - '@giscus/react@3.1.0': - resolution: {integrity: sha512-0TCO2TvL43+oOdyVVGHDItwxD1UMKP2ZYpT6gXmhFOqfAJtZxTzJ9hkn34iAF/b6YzyJ4Um89QIt9z/ajmAEeg==} - peerDependencies: - react: ^16 || ^17 || ^18 || ^19 - react-dom: ^16 || ^17 || ^18 || ^19 - - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - - '@iconify/utils@3.1.0': - resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} - - '@intlify/core-base@9.14.5': - resolution: {integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==} - engines: {node: '>= 16'} - - '@intlify/message-compiler@9.14.5': - resolution: {integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==} - engines: {node: '>= 16'} - - '@intlify/shared@9.14.5': - resolution: {integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==} - engines: {node: '>= 16'} - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@kurkle/color@0.3.4': - resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - - '@lit-labs/ssr-dom-shim@1.5.1': - resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} - - '@lit/reactive-element@2.1.2': - resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} - - '@lobehub/emojilib@1.0.0': - resolution: {integrity: sha512-s9KnjaPjsEefaNv150G3aifvB+J3P4eEKG+epY9zDPS2BeB6+V2jELWqAZll+nkogMaVovjEE813z3V751QwGw==} - - '@lobehub/fluent-emoji@4.1.0': - resolution: {integrity: sha512-R1MB2lfUkDvB7XAQdRzY75c1dx/tB7gEvBPaEEMarzKfCJWmXm7rheS6caVzmgwAlq5sfmTbxPL+un99sp//Yw==} - peerDependencies: - react: ^19.0.0 - react-dom: ^19.0.0 - - '@lobehub/icons@4.0.2': - resolution: {integrity: sha512-mYFEXXt7Z8iY8yLP5cDVctUPqlZUHWi5qzQCJiC646p7uiXhtpn93sRab/5pey+CYDh6BbRU6lhwiURu/SU5IA==} - peerDependencies: - '@lobehub/ui': ^4.3.3 - antd: ^6.1.1 - react: ^19.0.0 - react-dom: ^19.0.0 - - '@lobehub/ui@4.9.2': - resolution: {integrity: sha512-PT9PWXgT/PoIAyAPOaxF25enofBeeWL3zPD6CqlO3lSw1A1ENHC3+lG4lZsSquD+zBf3ATKLOqp5FuyoVWPUpA==} - peerDependencies: - '@lobehub/fluent-emoji': ^4.0.0 - '@lobehub/icons': ^4.0.0 - antd: ^6.1.1 - motion: ^12.0.0 - react: ^19.0.0 - react-dom: ^19.0.0 - - '@mdx-js/mdx@3.1.1': - resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - - '@mdx-js/react@3.1.1': - resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} - peerDependencies: - '@types/react': '>=16' - react: '>=16' - - '@mermaid-js/parser@1.1.0': - resolution: {integrity: sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@one-ini/wasm@0.1.1': - resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@primer/octicons@19.23.1': - resolution: {integrity: sha512-CzjGmxkmNhyst6EekrS3SJPdtzgIkUMP/LSJch65y99/kmiFXbO1a+q7zoYe3hnI9NaOM0IN+ydDIbOmd8YqcA==} - - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-portal@1.1.10': - resolution: {integrity: sha512-4kY9IVa6+9nJPsYmngK5Uk2kUmZnv7ChhHAFeQ5oaj8jrR1bIi3xww8nH71pz1/Ve4d/cXO3YxT8eikt1B0a8w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.1.4': - resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-tooltip@1.2.8': - resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.2.3': - resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - - '@rc-component/async-validator@5.1.0': - resolution: {integrity: sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==} - engines: {node: '>=14.x'} - - '@rc-component/cascader@1.10.0': - resolution: {integrity: sha512-D1XOKvbhdo9kX+cG1p8qJOnSq+sMK3L84iVYjGQIx950kJt0ixN+Xac75ykyK/AC8V3GUanjNK14Qkv149RrEw==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/checkbox@1.0.1': - resolution: {integrity: sha512-08yTH8m+bSm8TOqbybbJ9KiAuIATti6bDs2mVeSfu4QfEnyeF6X0enHVvD1NEAyuBWEAo56QtLe++MYs2D9XiQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/collapse@1.1.2': - resolution: {integrity: sha512-ilBYk1dLLJHu5Q74dF28vwtKUYQ42ZXIIDmqTuVy4rD8JQVvkXOs+KixVNbweyuIEtJYJ7+t+9GVD9dPc6N02w==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/color-picker@3.0.3': - resolution: {integrity: sha512-V7gFF9O7o5XwIWafdbOtqI4BUUkEUkgdBwp6favy3xajMX/2dDqytFaiXlcwrpq6aRyPLp5dKLAG5RFKLXMeGA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/context@2.0.1': - resolution: {integrity: sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/dialog@1.5.1': - resolution: {integrity: sha512-by4Sf/a3azcb89WayWuwG19/Y312xtu8N81HoVQQtnsBDylfs+dog98fTAvLinnpeoWG52m/M7QLRW6fXR3l1g==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/drawer@1.3.0': - resolution: {integrity: sha512-rE+sdXEmv2W25VBQ9daGbnb4J4hBIEKmdbj0b3xpY+K7TUmLXDIlSnoXraIbFZdGyek9WxxGKK887uRnFgI+pQ==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/dropdown@1.0.2': - resolution: {integrity: sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==} - peerDependencies: - react: '>=16.11.0' - react-dom: '>=16.11.0' - - '@rc-component/form@1.6.2': - resolution: {integrity: sha512-OgIn2RAoaSBqaIgzJf/X6iflIa9LpTozci1lagLBdURDFhGA370v0+T0tXxOi8YShMjTha531sFhwtnrv+EJaQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/image@1.5.3': - resolution: {integrity: sha512-/NR7QW9uCN8Ugar+xsHZOPvzPySfEhcW2/vLcr7VPRM+THZMrllMRv7LAUgW7ikR+Z67Ab67cgPp5K5YftpJsQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/input-number@1.6.2': - resolution: {integrity: sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/input@1.1.2': - resolution: {integrity: sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@rc-component/mentions@1.6.0': - resolution: {integrity: sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/menu@1.2.0': - resolution: {integrity: sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/mini-decimal@1.1.3': - resolution: {integrity: sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==} - engines: {node: '>=8.x'} - - '@rc-component/motion@1.1.6': - resolution: {integrity: sha512-aEQobs/YA0kqRvHIPjQvOytdtdRVyhf/uXAal4chBjxDu6odHckExJzjn2D+Ju1aKK6hx3pAs6BXdV9+86xkgQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/mutate-observer@2.0.1': - resolution: {integrity: sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/notification@1.2.0': - resolution: {integrity: sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/overflow@1.0.0': - resolution: {integrity: sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/pagination@1.2.0': - resolution: {integrity: sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/picker@1.9.1': - resolution: {integrity: sha512-9FBYYsvH3HMLICaPDA/1Th5FLaDkFa7qAtangIdlhKb3ZALaR745e9PsOhheJb6asS4QXc12ffiAcjdkZ4C5/g==} - engines: {node: '>=12.x'} - peerDependencies: - date-fns: '>= 2.x' - dayjs: '>= 1.x' - luxon: '>= 3.x' - moment: '>= 2.x' - react: '>=16.9.0' - react-dom: '>=16.9.0' - peerDependenciesMeta: - date-fns: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true - - '@rc-component/portal@1.1.2': - resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/portal@2.2.0': - resolution: {integrity: sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ==} - engines: {node: '>=12.x'} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/progress@1.0.2': - resolution: {integrity: sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/qrcode@1.1.1': - resolution: {integrity: sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/rate@1.0.1': - resolution: {integrity: sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/resize-observer@1.1.2': - resolution: {integrity: sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/segmented@1.3.0': - resolution: {integrity: sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@rc-component/select@1.4.0': - resolution: {integrity: sha512-DDCsUkx3lHAO42fyPiBADzZgbqOp3gepjBCusuy6DDN51Vx73cwX0aqsid1asxpIwHPMYGgYg+wXbLi4YctzLQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '*' - react-dom: '*' - - '@rc-component/slider@1.0.1': - resolution: {integrity: sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/steps@1.2.2': - resolution: {integrity: sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/switch@1.0.3': - resolution: {integrity: sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/table@1.9.1': - resolution: {integrity: sha512-FVI5ZS/GdB3BcgexfCYKi3iHhZS3Fr59EtsxORszYGrfpH1eWr33eDNSYkVfLI6tfJ7vftJDd9D5apfFWqkdJg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/tabs@1.7.0': - resolution: {integrity: sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/textarea@1.1.2': - resolution: {integrity: sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/tooltip@1.4.0': - resolution: {integrity: sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/tour@2.2.1': - resolution: {integrity: sha512-BUCrVikGJsXli38qlJ+h2WyDD6dYxzDA9dV3o0ij6gYhAq6ooT08SUMWOikva9v4KZ2BEuluGl5bPcsjrSoBgQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/tree-select@1.5.0': - resolution: {integrity: sha512-1nBAMreFJXkCIeZlWG0l+6i0jLWzlmmRv/TrtZjLkoq8WmpzSuDhP32YroC7rAhGFR34thpHkvCedPzBXIL/XQ==} - peerDependencies: - react: '*' - react-dom: '*' - - '@rc-component/tree@1.1.0': - resolution: {integrity: sha512-HZs3aOlvFgQdgrmURRc/f4IujiNBf4DdEeXUlkS0lPoLlx9RoqsZcF0caXIAMVb+NaWqKtGQDnrH8hqLCN5zlA==} - engines: {node: '>=10.x'} - peerDependencies: - react: '*' - react-dom: '*' - - '@rc-component/trigger@2.3.1': - resolution: {integrity: sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/trigger@3.9.0': - resolution: {integrity: sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/upload@1.1.0': - resolution: {integrity: sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/util@1.10.1': - resolution: {integrity: sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/util@1.7.0': - resolution: {integrity: sha512-tIvIGj4Vl6fsZFvWSkYw9sAfiCKUXMyhVz6kpKyZbwyZyRPqv2vxYZROdaO1VB4gqTNvUZFXh6i3APUiterw5g==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - '@rc-component/virtual-list@1.0.2': - resolution: {integrity: sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rollup/rollup-android-arm-eabi@4.54.0': - resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.54.0': - resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.54.0': - resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.54.0': - resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.54.0': - resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.54.0': - resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.54.0': - resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.54.0': - resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.54.0': - resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.54.0': - resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.54.0': - resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-gnu@4.54.0': - resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-gnu@4.54.0': - resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.54.0': - resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.54.0': - resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.54.0': - resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.54.0': - resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openharmony-arm64@4.54.0': - resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.54.0': - resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.54.0': - resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.54.0': - resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.54.0': - resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} - cpu: [x64] - os: [win32] - - '@shikijs/core@3.23.0': - resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} - - '@shikijs/engine-javascript@3.23.0': - resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} - - '@shikijs/engine-oniguruma@3.23.0': - resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} - - '@shikijs/langs@3.23.0': - resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} - - '@shikijs/themes@3.23.0': - resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - - '@shikijs/transformers@3.23.0': - resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==} - - '@shikijs/types@3.23.0': - resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@splinetool/runtime@0.9.526': - resolution: {integrity: sha512-qznHbXA5aKwDbCgESAothCNm1IeEZcmNWG145p5aXj4w5uoqR1TZ9qkTHTKLTsUbHeitCwdhzmRqan1kxboLgQ==} - - '@stitches/react@1.2.8': - resolution: {integrity: sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA==} - peerDependencies: - react: '>= 16.3.0' - - '@stripe/stripe-js@9.0.1': - resolution: {integrity: sha512-un0URSosrW7wNr7xZ5iI2mC9mdeXZ3KERoVlA2RdmeLXYxHUPXq0yHzir2n/MtyXXEdSaELtz4WXGS6dzPEeKA==} - engines: {node: '>=12.16'} - - '@tanstack/virtual-core@3.13.23': - resolution: {integrity: sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==} - - '@tanstack/vue-virtual@3.13.23': - resolution: {integrity: sha512-b5jPluAR6U3eOq6GWAYSpj3ugnAIZgGR0e6aGAgyRse0Yu6MVQQ0ZWm9SArSXWtageogn6bkVD8D//c4IjW3xQ==} - peerDependencies: - vue: ^2.7.0 || ^3.0.0 - - '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} - - '@types/d3-axis@3.0.6': - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - - '@types/d3-brush@3.0.6': - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - - '@types/d3-chord@3.0.6': - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - - '@types/d3-contour@3.0.6': - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - - '@types/d3-delaunay@6.0.4': - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - - '@types/d3-dispatch@3.0.7': - resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} - - '@types/d3-drag@3.0.7': - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - - '@types/d3-dsv@3.0.7': - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - - '@types/d3-fetch@3.0.7': - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - - '@types/d3-force@3.0.10': - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} - - '@types/d3-format@3.0.4': - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - - '@types/d3-geo@3.1.0': - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - - '@types/d3-hierarchy@3.1.7': - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} - - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - - '@types/d3-polygon@3.0.2': - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - - '@types/d3-quadtree@3.0.6': - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - - '@types/d3-random@3.0.3': - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} - - '@types/d3-scale-chromatic@3.1.0': - resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} - - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - - '@types/d3-selection@3.0.11': - resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} - - '@types/d3-shape@3.1.8': - resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} - - '@types/d3-time-format@4.0.3': - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} - - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - - '@types/d3-transition@3.0.9': - resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} - - '@types/d3-zoom@3.0.8': - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - - '@types/d3@7.4.3': - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - - '@types/debug@4.1.13': - resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} - - '@types/dompurify@3.2.0': - resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} - deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. - - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/file-saver@2.0.7': - resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} - - '@types/geojson@7946.0.16': - resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - - '@types/js-cookie@3.0.6': - resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} - - '@types/katex@0.16.8': - resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/mdx@2.0.13': - resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node@20.19.27': - resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} - - '@types/parse-json@4.0.2': - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - - '@types/qrcode@1.5.6': - resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} - - '@types/react@19.2.7': - resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} - - '@types/sortablejs@1.15.9': - resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==} - - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@types/web-bluetooth@0.0.20': - resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - - '@typescript-eslint/eslint-plugin@7.18.0': - resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@7.18.0': - resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@7.18.0': - resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/type-utils@7.18.0': - resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@7.18.0': - resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@typescript-eslint/typescript-estree@7.18.0': - resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@7.18.0': - resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - - '@typescript-eslint/visitor-keys@7.18.0': - resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} - engines: {node: ^18.18.0 || >=20.0.0} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - - '@upsetjs/venn.js@2.0.0': - resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} - - '@use-gesture/core@10.3.1': - resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} - - '@use-gesture/react@10.3.1': - resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} - peerDependencies: - react: '>= 16.8.0' - - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vue: ^3.2.25 - - '@vitest/coverage-v8@2.1.9': - resolution: {integrity: sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==} - peerDependencies: - '@vitest/browser': 2.1.9 - vitest: 2.1.9 - peerDependenciesMeta: - '@vitest/browser': - optional: true - - '@vitest/expect@2.1.9': - resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} - - '@vitest/mocker@2.1.9': - resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} - peerDependencies: - msw: ^2.4.9 - vite: ^5.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/pretty-format@2.1.9': - resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - - '@vitest/runner@2.1.9': - resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} - - '@vitest/snapshot@2.1.9': - resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - - '@vitest/spy@2.1.9': - resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} - - '@vitest/utils@2.1.9': - resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} - - '@volar/language-core@2.4.15': - resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} - - '@volar/source-map@2.4.15': - resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} - - '@volar/typescript@2.4.15': - resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} - - '@vue/compiler-core@3.5.26': - resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==} - - '@vue/compiler-dom@3.5.26': - resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==} - - '@vue/compiler-sfc@3.5.26': - resolution: {integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==} - - '@vue/compiler-ssr@3.5.26': - resolution: {integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==} - - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} - - '@vue/devtools-api@6.6.4': - resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - - '@vue/language-core@2.2.12': - resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@vue/reactivity@3.5.26': - resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==} - - '@vue/runtime-core@3.5.26': - resolution: {integrity: sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==} - - '@vue/runtime-dom@3.5.26': - resolution: {integrity: sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==} - - '@vue/server-renderer@3.5.26': - resolution: {integrity: sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==} - peerDependencies: - vue: 3.5.26 - - '@vue/shared@3.5.26': - resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} - - '@vue/test-utils@2.4.6': - resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} - - '@vueuse/core@10.11.1': - resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} - - '@vueuse/metadata@10.11.1': - resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} - - '@vueuse/shared@10.11.1': - resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} - - abbrev@2.0.0: - resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true - - adler-32@1.3.1: - resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} - engines: {node: '>=0.8'} - - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - - ahooks@3.9.7: - resolution: {integrity: sha512-S0lvzhbdlhK36RFBkGv+RbOM/dbbweym+BIHM/bwwuWVSVN5TuVErHPMWo4w0t1NDYg5KPp2iEf7Y7E5LASYiw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - alien-signals@1.0.13: - resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - antd-style@4.1.0: - resolution: {integrity: sha512-vnPBGg0OVlSz90KRYZhxd89aZiOImTiesF+9MQqN8jsLGZUQTjbP04X9jTdEfsztKUuMbBWg/RmB/wHTakbtMQ==} - peerDependencies: - antd: '>=6.0.0' - react: '>=18' - - antd@6.1.3: - resolution: {integrity: sha512-kvaLtOm0UwCIdtR424/Mo6pyJxN34/6003e1io3GIKWQOdlddplFylv767iGxXLMrxfNoQmxuNJcF1miFbxCZQ==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - assign-symbols@1.0.0: - resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} - engines: {node: '>=0.10.0'} - - astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} - hasBin: true - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - attr-accept@2.2.5: - resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} - engines: {node: '>=4'} - - autoprefixer@10.4.23: - resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - - axios@1.16.0: - resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} - - babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} - - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - baseline-browser-mapping@2.9.11: - resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} - hasBin: true - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - caniuse-lite@1.0.30001761: - resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - cfb@1.2.2: - resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} - engines: {node: '>=0.8'} - - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} - engines: {node: '>=18'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - - chart.js@4.5.1: - resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} - engines: {pnpm: '>=8'} - - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} - - chevrotain-allstar@0.4.1: - resolution: {integrity: sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==} - peerDependencies: - chevrotain: ^12.0.0 - - chevrotain@12.0.0: - resolution: {integrity: sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==} - engines: {node: '>=22.0.0'} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - chroma-js@3.2.0: - resolution: {integrity: sha512-os/OippSlX1RlWWr+QDPcGUZs0uoqr32urfxESG9U93lhUfbnlyckte84Q8P1UQY/qth983AS1JONKmLS4T0nw==} - - class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - - classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - codepage@1.15.0: - resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} - engines: {node: '>=0.8'} - - collapse-white-space@2.1.0: - resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - - config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - - convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - - cose-base@1.0.3: - resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} - - cose-base@2.2.0: - resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - - cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - - crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} - engines: {node: '>=18'} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - cytoscape-cose-bilkent@4.1.0: - resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape-fcose@2.2.0: - resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape@3.33.2: - resolution: {integrity: sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw==} - engines: {node: '>=0.10'} - - d3-array@2.12.1: - resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} - - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-axis@3.0.0: - resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} - engines: {node: '>=12'} - - d3-brush@3.0.0: - resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} - engines: {node: '>=12'} - - d3-chord@3.0.1: - resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-contour@4.0.2: - resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} - engines: {node: '>=12'} - - d3-delaunay@6.0.4: - resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} - engines: {node: '>=12'} - - d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - - d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} - - d3-dsv@3.0.1: - resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} - engines: {node: '>=12'} - hasBin: true - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-fetch@3.0.1: - resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} - engines: {node: '>=12'} - - d3-force@3.0.0: - resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} - engines: {node: '>=12'} - - d3-format@3.1.2: - resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} - engines: {node: '>=12'} - - d3-geo@3.1.1: - resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} - engines: {node: '>=12'} - - d3-hierarchy@3.1.2: - resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@1.0.9: - resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-polygon@3.0.1: - resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} - engines: {node: '>=12'} - - d3-quadtree@3.0.1: - resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} - engines: {node: '>=12'} - - d3-random@3.0.1: - resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} - engines: {node: '>=12'} - - d3-sankey@0.12.3: - resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} - - d3-scale-chromatic@3.1.0: - resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - - d3-shape@1.3.7: - resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} - - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} - - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - - d3-transition@3.0.1: - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 - - d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} - - d3@7.9.0: - resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} - engines: {node: '>=12'} - - dagre-d3-es@7.0.14: - resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} - - data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} - - dayjs@1.11.20: - resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} - - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - - decode-named-character-reference@1.3.0: - resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - - decode-uri-component@0.4.1: - resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} - engines: {node: '>=14.16'} - - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - delaunator@5.1.0: - resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - - dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - dompurify@3.3.1: - resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} - - dompurify@3.3.3: - resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} - - driver.js@1.4.0: - resolution: {integrity: sha512-Gm64jm6PmcU+si21sQhBrTAM1JvUrR0QhNmjkprNLxohOBzul9+pNHXgQaT9lW84gwg9GMLB3NZGuGolsz5uew==} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - editorconfig@1.0.4: - resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} - engines: {node: '>=14'} - hasBin: true - - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} - - emoji-mart@5.6.0: - resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==} - - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - - entities@7.0.0: - resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} - engines: {node: '>=0.12'} - - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-toolkit@1.45.1: - resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} - - esast-util-from-estree@2.0.0: - resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} - - esast-util-from-js@2.0.1: - resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - eslint-plugin-vue@9.33.0: - resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-util-attach-comments@3.0.0: - resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} - - estree-util-build-jsx@3.0.1: - resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} - - estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - - estree-util-scope@1.0.0: - resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} - - estree-util-to-js@2.0.0: - resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} - - estree-util-visit@2.0.0: - resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - - extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - - extend-shallow@3.0.2: - resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} - engines: {node: '>=0.10.0'} - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - - file-saver@2.0.5: - resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} - - file-selector@0.5.0: - resolution: {integrity: sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA==} - engines: {node: '>= 10'} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - filter-obj@5.1.0: - resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} - engines: {node: '>=14.16'} - - find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - for-in@1.0.2: - resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} - engines: {node: '>=0.10.0'} - - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - frac@1.1.2: - resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} - engines: {node: '>=0.8'} - - fraction.js@5.3.4: - resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - - framer-motion@12.38.0: - resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - react: - optional: true - react-dom: - optional: true - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-east-asian-width@1.5.0: - resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} - engines: {node: '>=18'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-value@2.0.6: - resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} - engines: {node: '>=0.10.0'} - - giscus@1.6.0: - resolution: {integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - hasBin: true - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - hachure-fill@0.5.2: - resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hast-util-from-dom@5.0.1: - resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} - - hast-util-from-html-isomorphic@2.0.0: - resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} - - hast-util-from-html@2.0.3: - resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} - - hast-util-from-parse5@8.0.3: - resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - - hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - - hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - - hast-util-raw@9.1.0: - resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} - - hast-util-to-estree@3.1.3: - resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - - hast-util-to-parse5@8.0.1: - resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} - - hast-util-to-text@4.0.2: - resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - hastscript@9.0.1: - resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - - html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - html-url-attributes@3.0.1: - resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - immer@11.1.4: - resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} - - internmap@1.0.1: - resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} - - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - intersection-observer@0.12.2: - resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} - deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. - - is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - - is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - is-extendable@1.0.1: - resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} - engines: {node: '>=0.10.0'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - - is-mobile@5.0.0: - resolution: {integrity: sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - - is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isobject@3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} - engines: {node: '>=0.10.0'} - - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} - hasBin: true - - js-beautify@1.15.4: - resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} - engines: {node: '>=14'} - hasBin: true - - js-cookie@3.0.7: - resolution: {integrity: sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==} - engines: {node: '>=20'} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - jsdom@24.1.3: - resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} - engines: {node: '>=18'} - peerDependencies: - canvas: ^2.11.2 - peerDependenciesMeta: - canvas: - optional: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json2mq@0.2.0: - resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} - - katex@0.16.45: - resolution: {integrity: sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==} - hasBin: true - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - khroma@2.1.0: - resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - - langium@4.2.2: - resolution: {integrity: sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==} - engines: {node: '>=20.10.0', npm: '>=10.2.3'} - - layout-base@1.0.2: - resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} - - layout-base@2.0.1: - resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - - leva@0.10.1: - resolution: {integrity: sha512-BcjnfUX8jpmwZUz2L7AfBtF9vn4ggTH33hmeufDULbP3YgNZ/C+ss/oO3stbrqRQyaOmRwy70y7BGTGO81S3rA==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - lit-element@4.2.2: - resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} - - lit-html@3.3.2: - resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==} - - lit@3.3.2: - resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash-es@4.18.1: - resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lucide-react@0.469.0: - resolution: {integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - lucide-react@0.562.0: - resolution: {integrity: sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - markdown-extensions@2.0.0: - resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} - engines: {node: '>=16'} - - markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - - marked@16.4.2: - resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} - engines: {node: '>= 20'} - hasBin: true - - marked@17.0.1: - resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==} - engines: {node: '>= 20'} - hasBin: true - - marked@17.0.6: - resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==} - engines: {node: '>= 20'} - hasBin: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - - mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} - - mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - - mdast-util-gfm-footnote@2.1.0: - resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} - - mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - - mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - - mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - - mdast-util-gfm@3.1.0: - resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} - - mdast-util-math@3.0.0: - resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} - - mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - - mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} - - mdast-util-mdx@3.0.0: - resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} - - mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} - - mdast-util-newline-to-break@2.0.0: - resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - - mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - - merge-value@1.0.0: - resolution: {integrity: sha512-fJMmvat4NeKz63Uv9iHWcPDjCWcCkoiRoajRTEO8hlhUC6rwaHg0QCF9hBOTjZmm4JuglPckPSTtcuJL5kp0TQ==} - engines: {node: '>=0.10.0'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - mermaid@11.14.0: - resolution: {integrity: sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==} - - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-cjk-friendly-util@2.1.1: - resolution: {integrity: sha512-egs6+12JU2yutskHY55FyR48ZiEcFOJFyk9rsiyIhcJ6IvWB6ABBqVrBw8IobqJTDZ/wdSr9eoXDPb5S2nW1bg==} - engines: {node: '>=16'} - peerDependencies: - micromark-util-types: '*' - peerDependenciesMeta: - micromark-util-types: - optional: true - - micromark-extension-cjk-friendly@1.2.3: - resolution: {integrity: sha512-gRzVLUdjXBLX6zNPSnHGDoo+ZTp5zy+MZm0g3sv+3chPXY7l9gW+DnrcHcZh/jiPR6MjPKO4AEJNp4Aw6V9z5Q==} - engines: {node: '>=16'} - peerDependencies: - micromark: ^4.0.0 - micromark-util-types: ^2.0.0 - peerDependenciesMeta: - micromark-util-types: - optional: true - - micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - - micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - - micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - - micromark-extension-gfm-table@2.1.1: - resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} - - micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - - micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} - - micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - - micromark-extension-math@3.1.0: - resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} - - micromark-extension-mdx-expression@3.0.1: - resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} - - micromark-extension-mdx-jsx@3.0.2: - resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} - - micromark-extension-mdx-md@2.0.0: - resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} - - micromark-extension-mdxjs-esm@3.0.0: - resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} - - micromark-extension-mdxjs@3.0.0: - resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} - - micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - - micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - - micromark-factory-mdx-expression@2.0.3: - resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} - - micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - - micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - - micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - - micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - - micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - - micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - - micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-events-to-acorn@2.0.3: - resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} - - micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - - micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - - micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@9.0.1: - resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} - engines: {node: '>=16 || 14 >=14.17'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - mixin-deep@1.3.2: - resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} - engines: {node: '>=0.10.0'} - - mlly@1.8.2: - resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} - - motion-dom@12.38.0: - resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} - - motion-utils@12.36.0: - resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} - - motion@12.23.26: - resolution: {integrity: sha512-Ll8XhVxY8LXMVYTCfme27WH2GjBrCIzY4+ndr5QKxsK+YwCtOi2B/oBi5jcIbik5doXuWT/4KKDOVAZJkeY5VQ==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - react: - optional: true - react-dom: - optional: true - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - muggle-string@0.4.1: - resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - - nopt@7.2.1: - resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - numeral@2.0.6: - resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==} - - nwsapi@2.2.23: - resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - - on-change@4.0.2: - resolution: {integrity: sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - oniguruma-parser@0.12.1: - resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} - - oniguruma-to-es@4.3.5: - resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - package-manager-detector@1.6.0: - resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-data-parser@0.1.0: - resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pinia@2.3.1: - resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==} - peerDependencies: - typescript: '>=4.4.4' - vue: ^2.7.0 || ^3.5.11 - peerDependenciesMeta: - typescript: - optional: true - - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - - pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} - - points-on-curve@0.2.0: - resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} - - points-on-path@0.2.1: - resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - - polished@4.3.1: - resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} - engines: {node: '>=10'} - - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - - postcss-js@4.1.0: - resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@6.0.1: - resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} - engines: {node: '>= 18'} - peerDependencies: - jiti: '>=1.21.0' - postcss: '>=8.0.9' - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - jiti: - optional: true - postcss: - optional: true - tsx: - optional: true - yaml: - optional: true - - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - - proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - - proxy-from-env@2.1.0: - resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} - engines: {node: '>=10'} - - psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - qrcode@1.5.4: - resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} - engines: {node: '>=10.13.0'} - hasBin: true - - query-string@9.3.1: - resolution: {integrity: sha512-5fBfMOcDi5SA9qj5jZhWAcTtDfKF5WFdd2uD9nVNlbxVv1baq65aALy6qofpNEGELHvisjjasxQp7BlM9gvMzw==} - engines: {node: '>=18'} - - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - rc-collapse@4.0.0: - resolution: {integrity: sha512-SwoOByE39/3oIokDs/BnkqI+ltwirZbP8HZdq1/3SkPSBi7xDdvWHTp7cpNI9ullozkR6mwTWQi6/E/9huQVrA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-dialog@9.6.0: - resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-footer@0.6.8: - resolution: {integrity: sha512-JBZ+xcb6kkex8XnBd4VHw1ZxjV6kmcwUumSHaIFdka2qzMCo7Klcy4sI6G0XtUpG/vtpislQCc+S9Bc+NLHYMg==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-image@7.12.0: - resolution: {integrity: sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input-number@9.5.0: - resolution: {integrity: sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input@1.8.0: - resolution: {integrity: sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-menu@9.16.1: - resolution: {integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-motion@2.9.5: - resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-overflow@1.5.0: - resolution: {integrity: sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-resize-observer@1.4.3: - resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-util@5.44.4: - resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - re-resizable@6.11.2: - resolution: {integrity: sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==} - peerDependencies: - react: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-avatar-editor@14.0.0: - resolution: {integrity: sha512-NaQM3oo4u0a1/Njjutc2FjwKX35vQV+t6S8hovsbAlMpBN1ntIwP/g+Yr9eDIIfaNtRXL0AqboTnPmRxhD/i8A==} - peerDependencies: - react: ^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^0.14.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-colorful@5.6.1: - resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - react-dom@19.2.3: - resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} - peerDependencies: - react: ^19.2.3 - - react-draggable@4.5.0: - resolution: {integrity: sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' - - react-dropzone@12.1.0: - resolution: {integrity: sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog==} - engines: {node: '>= 10.13'} - peerDependencies: - react: '>= 16.8' - - react-error-boundary@6.1.1: - resolution: {integrity: sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - - react-fast-compare@3.2.2: - resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - - react-hotkeys-hook@5.2.4: - resolution: {integrity: sha512-BgKg+A1+TawkYluh5Bo4cTmcgMN5L29uhJbDUQdHwPX+qgXRjIPYU5kIDHyxnAwCkCBiu9V5OpB2mpyeluVF2A==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react-markdown@10.1.0: - resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} - peerDependencies: - '@types/react': '>=18' - react: '>=18' - - react-merge-refs@3.0.2: - resolution: {integrity: sha512-MSZAfwFfdbEvwkKWP5EI5chuLYnNUxNS7vyS0i1Jp+wtd8J4Ga2ddzhaE68aMol2Z4vCnRM/oGOo1a3V75UPlw==} - peerDependencies: - react: '>=16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0' - peerDependenciesMeta: - react: - optional: true - - react-rnd@10.5.3: - resolution: {integrity: sha512-s/sIT3pGZnQ+57egijkTp9mizjIWrJz68Pq6yd+F/wniFY3IriML18dUXnQe/HP9uMiJ+9MAp44hljG99fZu6Q==} - peerDependencies: - react: '>=16.3.0' - react-dom: '>=16.3.0' - - react-zoom-pan-pinch@3.7.0: - resolution: {integrity: sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==} - engines: {node: '>=8', npm: '>=5'} - peerDependencies: - react: '*' - react-dom: '*' - - react@19.2.3: - resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} - engines: {node: '>=0.10.0'} - - read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - recma-build-jsx@1.0.0: - resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} - - recma-jsx@1.0.1: - resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - recma-parse@1.0.0: - resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} - - recma-stringify@1.0.0: - resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} - - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@6.1.0: - resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - - rehype-github-alerts@4.2.0: - resolution: {integrity: sha512-6di6kEu9WUHKLKrkKG2xX6AOuaCMGghg0Wq7MEuM/jBYUPVIq6PJpMe00dxMfU+/YSBtDXhffpDimgDi+BObIQ==} - - rehype-katex@7.0.1: - resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} - - rehype-raw@7.0.0: - resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} - - rehype-recma@1.0.0: - resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} - - remark-breaks@4.0.0: - resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} - - remark-cjk-friendly@1.2.3: - resolution: {integrity: sha512-UvAgxwlNk+l9Oqgl/9MWK2eWRS7zgBW/nXX9AthV7nd/3lNejF138E7Xbmk9Zs4WjTJGs721r7fAEc7tNFoH7g==} - engines: {node: '>=16'} - peerDependencies: - '@types/mdast': ^4.0.0 - unified: ^11.0.0 - peerDependenciesMeta: - '@types/mdast': - optional: true - - remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} - - remark-github@12.0.0: - resolution: {integrity: sha512-ByefQKFN184LeiGRCabfl7zUJsdlMYWEhiLX1gpmQ11yFg6xSuOTW7LVCv0oc1x+YvUMJW23NU36sJX2RWGgvg==} - - remark-math@6.0.0: - resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} - - remark-mdx@3.1.1: - resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} - - remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - - remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - - remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - - resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} - hasBin: true - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - robust-predicates@3.0.3: - resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} - - rollup@4.54.0: - resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - roughjs@4.6.6: - resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - - rrweb-cssom@0.7.1: - resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} - - rrweb-cssom@0.8.0: - resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rw@1.3.3: - resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - screenfull@5.2.0: - resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} - engines: {node: '>=0.10.0'} - - scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} - - semver-compare@1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-value@2.0.1: - resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} - engines: {node: '>=0.10.0'} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shiki-stream@0.1.4: - resolution: {integrity: sha512-4pz6JGSDmVTTkPJ/ueixHkFAXY4ySCc+unvCaDZV7hqq/sdJZirRxgIXSuNSKgiFlGTgRR97sdu2R8K55sPsrw==} - peerDependencies: - react: ^19.0.0 - solid-js: ^1.9.0 - vue: ^3.2.0 - peerDependenciesMeta: - react: - optional: true - solid-js: - optional: true - vue: - optional: true - - shiki@3.23.0: - resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - split-on-first@3.0.0: - resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} - engines: {node: '>=12'} - - split-string@3.1.0: - resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} - engines: {node: '>=0.10.0'} - - ssf@0.11.2: - resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} - engines: {node: '>=0.8'} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - - string-convert@0.2.1: - resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - style-to-js@1.1.21: - resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} - - style-to-object@1.0.14: - resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - - stylis@4.2.0: - resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - - sucrase@3.35.1: - resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - swr@2.4.1: - resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==} - peerDependencies: - react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} - - tailwindcss@3.4.19: - resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} - engines: {node: '>=14.0.0'} - hasBin: true - - test-exclude@7.0.1: - resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} - engines: {node: '>=18'} - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - - throttle-debounce@5.0.2: - resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} - engines: {node: '>=12.22'} - - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - - tinyexec@1.1.1: - resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} - engines: {node: '>=18'} - - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} - engines: {node: '>=14.0.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - to-vfile@8.0.0: - resolution: {integrity: sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==} - - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} - - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - - ts-dedent@2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - - ts-md5@2.0.1: - resolution: {integrity: sha512-yF35FCoEOFBzOclSkMNEUbFQZuv89KEQ+5Xz03HrMSGUGB1+r+El+JiGOFwsP4p9RFNzwlrydYoTLvPOuICl9w==} - engines: {node: '>=18'} - - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} - engines: {node: '>=14.17'} - hasBin: true - - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-find-after@5.0.0: - resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-position-from-estree@2.0.0: - resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-remove-position@5.0.0: - resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - url-join@5.0.0: - resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - - use-merge-value@1.2.0: - resolution: {integrity: sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==} - peerDependencies: - react: '>= 16.x' - - use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - uuid@13.0.0: - resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} - hasBin: true - - v8n@1.5.1: - resolution: {integrity: sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==} - - vfile-location@5.0.3: - resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} - - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - vite-node@2.1.9: - resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite-plugin-checker@0.9.3: - resolution: {integrity: sha512-Tf7QBjeBtG7q11zG0lvoF38/2AVUzzhMNu+Wk+mcsJ00Rk/FpJ4rmUviVJpzWkagbU13cGXvKpt7CMiqtxVTbQ==} - engines: {node: '>=14.16'} - peerDependencies: - '@biomejs/biome': '>=1.7' - eslint: '>=7' - meow: ^13.2.0 - optionator: ^0.9.4 - stylelint: '>=16' - typescript: '*' - vite: '>=2.0.0' - vls: '*' - vti: '*' - vue-tsc: ~2.2.10 - peerDependenciesMeta: - '@biomejs/biome': - optional: true - eslint: - optional: true - meow: - optional: true - optionator: - optional: true - stylelint: - optional: true - typescript: - optional: true - vls: - optional: true - vti: - optional: true - vue-tsc: - optional: true - - vite@5.4.21: - resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitest@2.1.9: - resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.9 - '@vitest/ui': 2.1.9 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vscode-jsonrpc@8.2.0: - resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} - engines: {node: '>=14.0.0'} - - vscode-languageserver-protocol@3.17.5: - resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - - vscode-languageserver-textdocument@1.0.12: - resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} - - vscode-languageserver-types@3.17.5: - resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - - vscode-languageserver@9.0.1: - resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} - hasBin: true - - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - - vue-chartjs@5.3.3: - resolution: {integrity: sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==} - peerDependencies: - chart.js: ^4.1.1 - vue: ^3.0.0-0 || ^2.7.0 - - vue-component-type-helpers@2.2.12: - resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} - - vue-demi@0.14.10: - resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - vue-draggable-plus@0.6.1: - resolution: {integrity: sha512-FbtQ/fuoixiOfTZzG3yoPl4JAo9HJXRHmBQZFB9x2NYCh6pq0TomHf7g5MUmpaDYv+LU2n6BPq2YN9sBO+FbIg==} - peerDependencies: - '@types/sortablejs': ^1.15.0 - '@vue/composition-api': '*' - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - vue-i18n@9.14.5: - resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==} - engines: {node: '>= 16'} - peerDependencies: - vue: ^3.0.0 - - vue-router@4.6.4: - resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} - peerDependencies: - vue: ^3.5.0 - - vue-tsc@2.2.12: - resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} - hasBin: true - peerDependencies: - typescript: '>=5.0.0' - - vue@3.5.26: - resolution: {integrity: sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} - - web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - wmf@1.0.2: - resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} - engines: {node: '>=0.8'} - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - word@0.3.0: - resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} - engines: {node: '>=0.8'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xlsx@0.18.5: - resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} - engines: {node: '>=0.8'} - hasBin: true - - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - - xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} - - xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - zustand@3.7.2: - resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==} - engines: {node: '>=12.7.0'} - peerDependencies: - react: '>=16.8' - peerDependenciesMeta: - react: - optional: true - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@airwallex/airtracker@3.2.0': {} - - '@airwallex/components-sdk@1.30.2': - dependencies: - '@airwallex/airtracker': 3.2.0 - - '@alloc/quick-lru@5.2.0': {} - - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@ant-design/colors@8.0.1': - dependencies: - '@ant-design/fast-color': 3.0.1 - - '@ant-design/cssinjs-utils@2.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/cssinjs': 2.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@babel/runtime': 7.29.2 - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@ant-design/cssinjs@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - '@emotion/hash': 0.8.0 - '@emotion/unitless': 0.7.5 - '@rc-component/util': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - csstype: 3.2.3 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - stylis: 4.3.6 - - '@ant-design/cssinjs@2.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@emotion/hash': 0.8.0 - '@emotion/unitless': 0.7.5 - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - csstype: 3.2.3 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - stylis: 4.3.6 - - '@ant-design/fast-color@3.0.1': {} - - '@ant-design/icons-svg@4.4.2': {} - - '@ant-design/icons@6.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/colors': 8.0.1 - '@ant-design/icons-svg': 4.4.2 - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@ant-design/react-slick@2.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - clsx: 2.1.1 - json2mq: 0.2.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - throttle-debounce: 5.0.2 - - '@antfu/install-pkg@1.1.0': - dependencies: - package-manager-detector: 1.6.0 - tinyexec: 1.1.1 - - '@asamuzakjp/css-color@3.2.0': - dependencies: - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - lru-cache: 10.4.3 - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/generator@7.28.5': - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} - - '@babel/parser@7.28.5': - dependencies: - '@babel/types': 7.28.5 - - '@babel/runtime@7.28.4': {} - - '@babel/runtime@7.29.2': {} - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - - '@babel/traverse@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.28.5': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@base-ui/react@1.3.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.6(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@floating-ui/utils': 0.2.11 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@base-ui/utils@0.2.6(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@floating-ui/utils': 0.2.11 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@bcoe/v8-coverage@0.2.3': {} - - '@braintree/sanitize-url@7.1.2': {} - - '@chevrotain/cst-dts-gen@12.0.0': - dependencies: - '@chevrotain/gast': 12.0.0 - '@chevrotain/types': 12.0.0 - - '@chevrotain/gast@12.0.0': - dependencies: - '@chevrotain/types': 12.0.0 - - '@chevrotain/regexp-to-ast@12.0.0': {} - - '@chevrotain/types@12.0.0': {} - - '@chevrotain/utils@12.0.0': {} - - '@csstools/color-helpers@5.1.0': {} - - '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': - dependencies: - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': - dependencies: - '@csstools/color-helpers': 5.1.0 - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': - dependencies: - '@csstools/css-tokenizer': 3.0.4 - - '@csstools/css-tokenizer@3.0.4': {} - - '@dnd-kit/accessibility@3.1.1(react@19.2.3)': - dependencies: - react: 19.2.3 - tslib: 2.8.1 - - '@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@dnd-kit/accessibility': 3.1.1(react@19.2.3) - '@dnd-kit/utilities': 3.2.2(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - tslib: 2.8.1 - - '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)': - dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@dnd-kit/utilities': 3.2.2(react@19.2.3) - react: 19.2.3 - tslib: 2.8.1 - - '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)': - dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@dnd-kit/utilities': 3.2.2(react@19.2.3) - react: 19.2.3 - tslib: 2.8.1 - - '@dnd-kit/utilities@3.2.2(react@19.2.3)': - dependencies: - react: 19.2.3 - tslib: 2.8.1 - - '@emoji-mart/data@1.2.1': {} - - '@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@19.2.3)': - dependencies: - emoji-mart: 5.6.0 - react: 19.2.3 - - '@emotion/babel-plugin@11.13.5': - dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/runtime': 7.28.4 - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/serialize': 1.3.3 - babel-plugin-macros: 3.1.0 - convert-source-map: 1.9.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.2.0 - transitivePeerDependencies: - - supports-color - - '@emotion/cache@11.14.0': - dependencies: - '@emotion/memoize': 0.9.0 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - stylis: 4.2.0 - - '@emotion/css@11.13.5': - dependencies: - '@emotion/babel-plugin': 11.13.5 - '@emotion/cache': 11.14.0 - '@emotion/serialize': 1.3.3 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.2 - transitivePeerDependencies: - - supports-color - - '@emotion/hash@0.8.0': {} - - '@emotion/hash@0.9.2': {} - - '@emotion/is-prop-valid@1.4.0': - dependencies: - '@emotion/memoize': 0.9.0 - - '@emotion/memoize@0.9.0': {} - - '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@babel/runtime': 7.28.4 - '@emotion/babel-plugin': 11.13.5 - '@emotion/cache': 11.14.0 - '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - hoist-non-react-statics: 3.3.2 - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - transitivePeerDependencies: - - supports-color - - '@emotion/serialize@1.3.3': - dependencies: - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/unitless': 0.10.0 - '@emotion/utils': 1.4.2 - csstype: 3.2.3 - - '@emotion/sheet@1.4.0': {} - - '@emotion/unitless@0.10.0': {} - - '@emotion/unitless@0.7.5': {} - - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.3)': - dependencies: - react: 19.2.3 - - '@emotion/utils@1.4.2': {} - - '@emotion/weak-memoize@0.4.0': {} - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.2': {} - - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@8.57.1': {} - - '@floating-ui/core@1.7.5': - dependencies: - '@floating-ui/utils': 0.2.11 - - '@floating-ui/dom@1.7.6': - dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 - - '@floating-ui/react-dom@2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@floating-ui/dom': 1.7.6 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@floating-ui/react@0.27.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@floating-ui/utils': 0.2.11 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - tabbable: 6.4.0 - - '@floating-ui/utils@0.2.11': {} - - '@giscus/react@3.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - giscus: 1.6.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@humanwhocodes/config-array@0.13.0': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/object-schema@2.0.3': {} - - '@iconify/types@2.0.0': {} - - '@iconify/utils@3.1.0': - dependencies: - '@antfu/install-pkg': 1.1.0 - '@iconify/types': 2.0.0 - mlly: 1.8.2 - - '@intlify/core-base@9.14.5': - dependencies: - '@intlify/message-compiler': 9.14.5 - '@intlify/shared': 9.14.5 - - '@intlify/message-compiler@9.14.5': - dependencies: - '@intlify/shared': 9.14.5 - source-map-js: 1.2.1 - - '@intlify/shared@9.14.5': {} - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@kurkle/color@0.3.4': {} - - '@lit-labs/ssr-dom-shim@1.5.1': {} - - '@lit/reactive-element@2.1.2': - dependencies: - '@lit-labs/ssr-dom-shim': 1.5.1 - - '@lobehub/emojilib@1.0.0': {} - - '@lobehub/fluent-emoji@4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@lobehub/emojilib': 1.0.0 - antd-style: 4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - emoji-regex: 10.6.0 - es-toolkit: 1.45.1 - lucide-react: 0.562.0(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - url-join: 5.0.0 - transitivePeerDependencies: - - '@types/react' - - antd - - supports-color - - '@lobehub/icons@4.0.2(@lobehub/ui@4.9.2)(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@lobehub/ui': 4.9.2(@lobehub/fluent-emoji@4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@lobehub/icons@4.0.2)(@types/mdast@4.0.4)(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(micromark-util-types@2.0.2)(micromark@4.0.2)(motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.6.3)) - antd: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - antd-style: 4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - lucide-react: 0.469.0(react@19.2.3) - polished: 4.3.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - transitivePeerDependencies: - - '@types/react' - - supports-color - - '@lobehub/ui@4.9.2(@lobehub/fluent-emoji@4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@lobehub/icons@4.0.2)(@types/mdast@4.0.4)(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(micromark-util-types@2.0.2)(micromark@4.0.2)(motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.26(typescript@5.6.3))': - dependencies: - '@ant-design/cssinjs': 2.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@base-ui/react': 1.3.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@dnd-kit/core': 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) - '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) - '@dnd-kit/utilities': 3.2.2(react@19.2.3) - '@emoji-mart/data': 1.2.1 - '@emoji-mart/react': 1.1.1(emoji-mart@5.6.0)(react@19.2.3) - '@emotion/is-prop-valid': 1.4.0 - '@floating-ui/react': 0.27.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@giscus/react': 3.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@lobehub/fluent-emoji': 4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@lobehub/icons': 4.0.2(@lobehub/ui@4.9.2)(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@mdx-js/mdx': 3.1.1 - '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.3) - '@shikijs/core': 3.23.0 - '@shikijs/transformers': 3.23.0 - '@splinetool/runtime': 0.9.526 - ahooks: 3.9.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - antd: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - antd-style: 4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - chroma-js: 3.2.0 - class-variance-authority: 0.7.1 - clsx: 2.1.1 - dayjs: 1.11.20 - emoji-mart: 5.6.0 - es-toolkit: 1.45.1 - fast-deep-equal: 3.1.3 - immer: 11.1.4 - katex: 0.16.45 - leva: 0.10.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - lucide-react: 0.562.0(react@19.2.3) - marked: 17.0.6 - mermaid: 11.14.0 - motion: 12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - numeral: 2.0.6 - polished: 4.3.1 - query-string: 9.3.1 - rc-collapse: 4.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-footer: 0.6.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-image: 7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-input-number: 9.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-menu: 9.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - re-resizable: 6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-avatar-editor: 14.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-dom: 19.2.3(react@19.2.3) - react-error-boundary: 6.1.1(react@19.2.3) - react-hotkeys-hook: 5.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-markdown: 10.1.0(@types/react@19.2.7)(react@19.2.3) - react-merge-refs: 3.0.2(react@19.2.3) - react-rnd: 10.5.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-zoom-pan-pinch: 3.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rehype-github-alerts: 4.2.0 - rehype-katex: 7.0.1 - rehype-raw: 7.0.0 - remark-breaks: 4.0.0 - remark-cjk-friendly: 1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5) - remark-gfm: 4.0.1 - remark-github: 12.0.0 - remark-math: 6.0.0 - shiki: 3.23.0 - shiki-stream: 0.1.4(react@19.2.3)(vue@3.5.26(typescript@5.6.3)) - swr: 2.4.1(react@19.2.3) - ts-md5: 2.0.1 - unified: 11.0.5 - url-join: 5.0.0 - use-merge-value: 1.2.0(react@19.2.3) - uuid: 13.0.0 - transitivePeerDependencies: - - '@types/mdast' - - '@types/react' - - '@types/react-dom' - - micromark - - micromark-util-types - - solid-js - - supports-color - - vue - - '@mdx-js/mdx@3.1.1': - dependencies: - '@types/estree': 1.0.8 - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdx': 2.0.13 - acorn: 8.16.0 - collapse-white-space: 2.1.0 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-util-scope: 1.0.0 - estree-walker: 3.0.3 - hast-util-to-jsx-runtime: 2.3.6 - markdown-extensions: 2.0.0 - recma-build-jsx: 1.0.0 - recma-jsx: 1.0.1(acorn@8.16.0) - recma-stringify: 1.0.0 - rehype-recma: 1.0.0 - remark-mdx: 3.1.1 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - source-map: 0.7.6 - unified: 11.0.5 - unist-util-position-from-estree: 2.0.0 - unist-util-stringify-position: 4.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@types/mdx': 2.0.13 - '@types/react': 19.2.7 - react: 19.2.3 - - '@mermaid-js/parser@1.1.0': - dependencies: - langium: 4.2.2 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - - '@one-ini/wasm@0.1.1': {} - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@primer/octicons@19.23.1': - dependencies: - object-assign: 4.1.1 - - '@radix-ui/primitive@1.1.3': {} - - '@radix-ui/react-arrow@1.1.7(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.7)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-context@1.1.2(@types/react@19.2.7)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-dismissable-layer@1.1.11(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-id@1.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-popper@1.2.8(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-arrow': 1.1.7(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/rect': 1.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-portal@1.1.10(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-portal@1.1.9(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-presence@1.1.5(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-primitive@2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-primitive@2.1.4(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-slot@1.2.3(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-slot@1.2.4(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-tooltip@1.2.8(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-popper': 1.2.8(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-portal': 1.1.9(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-presence': 1.1.5(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.7)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.7)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/react-visually-hidden@1.2.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - - '@radix-ui/rect@1.1.1': {} - - '@rc-component/async-validator@5.1.0': - dependencies: - '@babel/runtime': 7.29.2 - - '@rc-component/cascader@1.10.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/select': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/checkbox@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/collapse@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/color-picker@3.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@ant-design/fast-color': 3.0.1 - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/context@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/dialog@1.5.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/drawer@1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/dropdown@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/form@1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/async-validator': 5.1.0 - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/image@1.5.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/input-number@1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/mini-decimal': 1.1.3 - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/input@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/mentions@1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/textarea': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/menu@1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/mini-decimal@1.1.3': - dependencies: - '@babel/runtime': 7.29.2 - - '@rc-component/motion@1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/mutate-observer@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/notification@1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/overflow@1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/pagination@1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/picker@1.9.1(dayjs@1.11.20)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - dayjs: 1.11.20 - - '@rc-component/portal@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/portal@2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/progress@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/qrcode@1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/rate@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/resize-observer@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/segmented@1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/select@1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/slider@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/steps@1.2.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/switch@1.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/table@1.9.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/context': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/tabs@1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/dropdown': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/textarea@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/tooltip@1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/tour@2.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/tree-select@1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/select': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/tree@1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/trigger@2.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/trigger@3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/portal': 2.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/upload@1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rc-component/util@1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - is-mobile: 5.0.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-is: 18.3.1 - - '@rc-component/util@1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - is-mobile: 5.0.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-is: 18.3.1 - - '@rc-component/virtual-list@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@rollup/rollup-android-arm-eabi@4.54.0': - optional: true - - '@rollup/rollup-android-arm64@4.54.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.54.0': - optional: true - - '@rollup/rollup-darwin-x64@4.54.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.54.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.54.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.54.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.54.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.54.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.54.0': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.54.0': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.54.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.54.0': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.54.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.54.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.54.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.54.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.54.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.54.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.54.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.54.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.54.0': - optional: true - - '@shikijs/core@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.5 - - '@shikijs/engine-oniguruma@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - - '@shikijs/themes@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - - '@shikijs/transformers@3.23.0': - dependencies: - '@shikijs/core': 3.23.0 - '@shikijs/types': 3.23.0 - - '@shikijs/types@3.23.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@splinetool/runtime@0.9.526': - dependencies: - on-change: 4.0.2 - semver-compare: 1.0.0 - - '@stitches/react@1.2.8(react@19.2.3)': - dependencies: - react: 19.2.3 - - '@stripe/stripe-js@9.0.1': {} - - '@tanstack/virtual-core@3.13.23': {} - - '@tanstack/vue-virtual@3.13.23(vue@3.5.26(typescript@5.6.3))': - dependencies: - '@tanstack/virtual-core': 3.13.23 - vue: 3.5.26(typescript@5.6.3) - - '@types/d3-array@3.2.2': {} - - '@types/d3-axis@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-brush@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-chord@3.0.6': {} - - '@types/d3-color@3.1.3': {} - - '@types/d3-contour@3.0.6': - dependencies: - '@types/d3-array': 3.2.2 - '@types/geojson': 7946.0.16 - - '@types/d3-delaunay@6.0.4': {} - - '@types/d3-dispatch@3.0.7': {} - - '@types/d3-drag@3.0.7': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-dsv@3.0.7': {} - - '@types/d3-ease@3.0.2': {} - - '@types/d3-fetch@3.0.7': - dependencies: - '@types/d3-dsv': 3.0.7 - - '@types/d3-force@3.0.10': {} - - '@types/d3-format@3.0.4': {} - - '@types/d3-geo@3.1.0': - dependencies: - '@types/geojson': 7946.0.16 - - '@types/d3-hierarchy@3.1.7': {} - - '@types/d3-interpolate@3.0.4': - dependencies: - '@types/d3-color': 3.1.3 - - '@types/d3-path@3.1.1': {} - - '@types/d3-polygon@3.0.2': {} - - '@types/d3-quadtree@3.0.6': {} - - '@types/d3-random@3.0.3': {} - - '@types/d3-scale-chromatic@3.1.0': {} - - '@types/d3-scale@4.0.9': - dependencies: - '@types/d3-time': 3.0.4 - - '@types/d3-selection@3.0.11': {} - - '@types/d3-shape@3.1.8': - dependencies: - '@types/d3-path': 3.1.1 - - '@types/d3-time-format@4.0.3': {} - - '@types/d3-time@3.0.4': {} - - '@types/d3-timer@3.0.2': {} - - '@types/d3-transition@3.0.9': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-zoom@3.0.8': - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - - '@types/d3@7.4.3': - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.7 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.10 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.1.1 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 - '@types/d3-scale': 4.0.9 - '@types/d3-scale-chromatic': 3.1.0 - '@types/d3-selection': 3.0.11 - '@types/d3-shape': 3.1.8 - '@types/d3-time': 3.0.4 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - - '@types/debug@4.1.13': - dependencies: - '@types/ms': 2.1.0 - - '@types/dompurify@3.2.0': - dependencies: - dompurify: 3.3.1 - - '@types/estree-jsx@1.0.5': - dependencies: - '@types/estree': 1.0.8 - - '@types/estree@1.0.8': {} - - '@types/file-saver@2.0.7': {} - - '@types/geojson@7946.0.16': {} - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/js-cookie@3.0.6': {} - - '@types/katex@0.16.8': {} - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdx@2.0.13': {} - - '@types/ms@2.1.0': {} - - '@types/node@20.19.27': - dependencies: - undici-types: 6.21.0 - - '@types/parse-json@4.0.2': {} - - '@types/qrcode@1.5.6': - dependencies: - '@types/node': 20.19.27 - - '@types/react@19.2.7': - dependencies: - csstype: 3.2.3 - - '@types/sortablejs@1.15.9': {} - - '@types/trusted-types@2.0.7': {} - - '@types/unist@2.0.11': {} - - '@types/unist@3.0.3': {} - - '@types/web-bluetooth@0.0.20': {} - - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.6.3) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 8.57.1 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3)': - dependencies: - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3 - eslint: 8.57.1 - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@7.18.0': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - - '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.6.3)': - dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3) - debug: 4.4.3 - eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@7.18.0': {} - - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3)': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 1.4.3(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.6.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) - eslint: 8.57.1 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@7.18.0': - dependencies: - '@typescript-eslint/types': 7.18.0 - eslint-visitor-keys: 3.4.3 - - '@ungap/structured-clone@1.3.0': {} - - '@upsetjs/venn.js@2.0.0': - optionalDependencies: - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - '@use-gesture/core@10.3.1': {} - - '@use-gesture/react@10.3.1(react@19.2.3)': - dependencies: - '@use-gesture/core': 10.3.1 - react: 19.2.3 - - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@20.19.27))(vue@3.5.26(typescript@5.6.3))': - dependencies: - vite: 5.4.21(@types/node@20.19.27) - vue: 3.5.26(typescript@5.6.3) - - '@vitest/coverage-v8@2.1.9(vitest@2.1.9(@types/node@20.19.27)(jsdom@24.1.3))': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 0.2.3 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.2.0 - magic-string: 0.30.21 - magicast: 0.3.5 - std-env: 3.10.0 - test-exclude: 7.0.1 - tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@20.19.27)(jsdom@24.1.3) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@2.1.9': - dependencies: - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.3.3 - tinyrainbow: 1.2.0 - - '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.27))': - dependencies: - '@vitest/spy': 2.1.9 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 5.4.21(@types/node@20.19.27) - - '@vitest/pretty-format@2.1.9': - dependencies: - tinyrainbow: 1.2.0 - - '@vitest/runner@2.1.9': - dependencies: - '@vitest/utils': 2.1.9 - pathe: 1.1.2 - - '@vitest/snapshot@2.1.9': - dependencies: - '@vitest/pretty-format': 2.1.9 - magic-string: 0.30.21 - pathe: 1.1.2 - - '@vitest/spy@2.1.9': - dependencies: - tinyspy: 3.0.2 - - '@vitest/utils@2.1.9': - dependencies: - '@vitest/pretty-format': 2.1.9 - loupe: 3.2.1 - tinyrainbow: 1.2.0 - - '@volar/language-core@2.4.15': - dependencies: - '@volar/source-map': 2.4.15 - - '@volar/source-map@2.4.15': {} - - '@volar/typescript@2.4.15': - dependencies: - '@volar/language-core': 2.4.15 - path-browserify: 1.0.1 - vscode-uri: 3.1.0 - - '@vue/compiler-core@3.5.26': - dependencies: - '@babel/parser': 7.28.5 - '@vue/shared': 3.5.26 - entities: 7.0.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.5.26': - dependencies: - '@vue/compiler-core': 3.5.26 - '@vue/shared': 3.5.26 - - '@vue/compiler-sfc@3.5.26': - dependencies: - '@babel/parser': 7.28.5 - '@vue/compiler-core': 3.5.26 - '@vue/compiler-dom': 3.5.26 - '@vue/compiler-ssr': 3.5.26 - '@vue/shared': 3.5.26 - estree-walker: 2.0.2 - magic-string: 0.30.21 - postcss: 8.5.6 - source-map-js: 1.2.1 - - '@vue/compiler-ssr@3.5.26': - dependencies: - '@vue/compiler-dom': 3.5.26 - '@vue/shared': 3.5.26 - - '@vue/compiler-vue2@2.7.16': - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - '@vue/devtools-api@6.6.4': {} - - '@vue/language-core@2.2.12(typescript@5.6.3)': - dependencies: - '@volar/language-core': 2.4.15 - '@vue/compiler-dom': 3.5.26 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.26 - alien-signals: 1.0.13 - minimatch: 9.0.5 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.6.3 - - '@vue/reactivity@3.5.26': - dependencies: - '@vue/shared': 3.5.26 - - '@vue/runtime-core@3.5.26': - dependencies: - '@vue/reactivity': 3.5.26 - '@vue/shared': 3.5.26 - - '@vue/runtime-dom@3.5.26': - dependencies: - '@vue/reactivity': 3.5.26 - '@vue/runtime-core': 3.5.26 - '@vue/shared': 3.5.26 - csstype: 3.2.3 - - '@vue/server-renderer@3.5.26(vue@3.5.26(typescript@5.6.3))': - dependencies: - '@vue/compiler-ssr': 3.5.26 - '@vue/shared': 3.5.26 - vue: 3.5.26(typescript@5.6.3) - - '@vue/shared@3.5.26': {} - - '@vue/test-utils@2.4.6': - dependencies: - js-beautify: 1.15.4 - vue-component-type-helpers: 2.2.12 - - '@vueuse/core@10.11.1(vue@3.5.26(typescript@5.6.3))': - dependencies: - '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.11.1 - '@vueuse/shared': 10.11.1(vue@3.5.26(typescript@5.6.3)) - vue-demi: 0.14.10(vue@3.5.26(typescript@5.6.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - '@vueuse/metadata@10.11.1': {} - - '@vueuse/shared@10.11.1(vue@3.5.26(typescript@5.6.3))': - dependencies: - vue-demi: 0.14.10(vue@3.5.26(typescript@5.6.3)) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - - abbrev@2.0.0: {} - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn-jsx@5.3.2(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - - acorn@8.15.0: {} - - acorn@8.16.0: {} - - adler-32@1.3.1: {} - - agent-base@7.1.4: {} - - ahooks@3.9.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - '@types/js-cookie': 3.0.6 - dayjs: 1.11.20 - intersection-observer: 0.12.2 - js-cookie: 3.0.7 - lodash: 4.18.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-fast-compare: 3.2.2 - resize-observer-polyfill: 1.5.1 - screenfull: 5.2.0 - tslib: 2.8.1 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - alien-signals@1.0.13: {} - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.3: {} - - antd-style@4.1.0(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@ant-design/cssinjs': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@babel/runtime': 7.28.4 - '@emotion/cache': 11.14.0 - '@emotion/css': 11.13.5 - '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) - '@emotion/serialize': 1.3.3 - '@emotion/utils': 1.4.2 - antd: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - use-merge-value: 1.2.0(react@19.2.3) - transitivePeerDependencies: - - '@types/react' - - react-dom - - supports-color - - antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@ant-design/colors': 8.0.1 - '@ant-design/cssinjs': 2.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/cssinjs-utils': 2.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/fast-color': 3.0.1 - '@ant-design/icons': 6.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@ant-design/react-slick': 2.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@babel/runtime': 7.29.2 - '@rc-component/cascader': 1.10.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/checkbox': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/collapse': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/color-picker': 3.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/dialog': 1.5.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/drawer': 1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/dropdown': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/form': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/image': 1.5.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/input-number': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/mentions': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/mutate-observer': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/notification': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/pagination': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/picker': 1.9.1(dayjs@1.11.20)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/progress': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/qrcode': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/rate': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/resize-observer': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/segmented': 1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/select': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/slider': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/steps': 1.2.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/switch': 1.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/table': 1.9.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tabs': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/textarea': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tooltip': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tour': 2.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/tree-select': 1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/trigger': 3.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/upload': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@rc-component/util': 1.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - clsx: 2.1.1 - dayjs: 1.11.20 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - scroll-into-view-if-needed: 3.1.0 - throttle-debounce: 5.0.2 - transitivePeerDependencies: - - date-fns - - luxon - - moment - - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@5.0.2: {} - - argparse@2.0.1: {} - - array-union@2.1.0: {} - - assertion-error@2.0.1: {} - - assign-symbols@1.0.0: {} - - astring@1.9.0: {} - - asynckit@0.4.0: {} - - attr-accept@2.2.5: {} - - autoprefixer@10.4.23(postcss@8.5.6): - dependencies: - browserslist: 4.28.1 - caniuse-lite: 1.0.30001761 - fraction.js: 5.3.4 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - axios@1.16.0: - dependencies: - follow-redirects: 1.16.0 - form-data: 4.0.5 - proxy-from-env: 2.1.0 - transitivePeerDependencies: - - debug - - babel-plugin-macros@3.1.0: - dependencies: - '@babel/runtime': 7.28.4 - cosmiconfig: 7.1.0 - resolve: 1.22.11 - - bail@2.0.2: {} - - balanced-match@1.0.2: {} - - baseline-browser-mapping@2.9.11: {} - - binary-extensions@2.3.0: {} - - boolbase@1.0.0: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001761 - electron-to-chromium: 1.5.267 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - - cac@6.7.14: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - callsites@3.1.0: {} - - camelcase-css@2.0.1: {} - - camelcase@5.3.1: {} - - caniuse-lite@1.0.30001761: {} - - ccount@2.0.1: {} - - cfb@1.2.2: - dependencies: - adler-32: 1.3.1 - crc-32: 1.2.2 - - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - - chart.js@4.5.1: - dependencies: - '@kurkle/color': 0.3.4 - - check-error@2.1.3: {} - - chevrotain-allstar@0.4.1(chevrotain@12.0.0): - dependencies: - chevrotain: 12.0.0 - lodash-es: 4.18.1 - - chevrotain@12.0.0: - dependencies: - '@chevrotain/cst-dts-gen': 12.0.0 - '@chevrotain/gast': 12.0.0 - '@chevrotain/regexp-to-ast': 12.0.0 - '@chevrotain/types': 12.0.0 - '@chevrotain/utils': 12.0.0 - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - chroma-js@3.2.0: {} - - class-variance-authority@0.7.1: - dependencies: - clsx: 2.1.1 - - classnames@2.5.1: {} - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - - clsx@2.1.1: {} - - codepage@1.15.0: {} - - collapse-white-space@2.1.0: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - colord@2.9.3: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@2.0.3: {} - - commander@10.0.1: {} - - commander@4.1.1: {} - - commander@7.2.0: {} - - commander@8.3.0: {} - - compute-scroll-into-view@3.1.1: {} - - concat-map@0.0.1: {} - - confbox@0.1.8: {} - - config-chain@1.1.13: - dependencies: - ini: 1.3.8 - proto-list: 1.2.4 - - convert-source-map@1.9.0: {} - - cose-base@1.0.3: - dependencies: - layout-base: 1.0.2 - - cose-base@2.2.0: - dependencies: - layout-base: 2.0.1 - - cosmiconfig@7.1.0: - dependencies: - '@types/parse-json': 4.0.2 - import-fresh: 3.3.1 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - - crc-32@1.2.2: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - cssesc@3.0.0: {} - - cssstyle@4.6.0: - dependencies: - '@asamuzakjp/css-color': 3.2.0 - rrweb-cssom: 0.8.0 - - csstype@3.2.3: {} - - cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.2): - dependencies: - cose-base: 1.0.3 - cytoscape: 3.33.2 - - cytoscape-fcose@2.2.0(cytoscape@3.33.2): - dependencies: - cose-base: 2.2.0 - cytoscape: 3.33.2 - - cytoscape@3.33.2: {} - - d3-array@2.12.1: - dependencies: - internmap: 1.0.1 - - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-axis@3.0.0: {} - - d3-brush@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3-chord@3.0.1: - dependencies: - d3-path: 3.1.0 - - d3-color@3.1.0: {} - - d3-contour@4.0.2: - dependencies: - d3-array: 3.2.4 - - d3-delaunay@6.0.4: - dependencies: - delaunator: 5.1.0 - - d3-dispatch@3.0.1: {} - - d3-drag@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-selection: 3.0.0 - - d3-dsv@3.0.1: - dependencies: - commander: 7.2.0 - iconv-lite: 0.6.3 - rw: 1.3.3 - - d3-ease@3.0.1: {} - - d3-fetch@3.0.1: - dependencies: - d3-dsv: 3.0.1 - - d3-force@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - - d3-format@3.1.2: {} - - d3-geo@3.1.1: - dependencies: - d3-array: 3.2.4 - - d3-hierarchy@3.1.2: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@1.0.9: {} - - d3-path@3.1.0: {} - - d3-polygon@3.0.1: {} - - d3-quadtree@3.0.1: {} - - d3-random@3.0.1: {} - - d3-sankey@0.12.3: - dependencies: - d3-array: 2.12.1 - d3-shape: 1.3.7 - - d3-scale-chromatic@3.1.0: - dependencies: - d3-color: 3.1.0 - d3-interpolate: 3.0.1 - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.2 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-selection@3.0.0: {} - - d3-shape@1.3.7: - dependencies: - d3-path: 1.0.9 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - d3-transition@3.0.1(d3-selection@3.0.0): - dependencies: - d3-color: 3.1.0 - d3-dispatch: 3.0.1 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-timer: 3.0.1 - - d3-zoom@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3@7.9.0: - dependencies: - d3-array: 3.2.4 - d3-axis: 3.0.0 - d3-brush: 3.0.0 - d3-chord: 3.0.1 - d3-color: 3.1.0 - d3-contour: 4.0.2 - d3-delaunay: 6.0.4 - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-dsv: 3.0.1 - d3-ease: 3.0.1 - d3-fetch: 3.0.1 - d3-force: 3.0.0 - d3-format: 3.1.2 - d3-geo: 3.1.1 - d3-hierarchy: 3.1.2 - d3-interpolate: 3.0.1 - d3-path: 3.1.0 - d3-polygon: 3.0.1 - d3-quadtree: 3.0.1 - d3-random: 3.0.1 - d3-scale: 4.0.2 - d3-scale-chromatic: 3.1.0 - d3-selection: 3.0.0 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - d3-timer: 3.0.1 - d3-transition: 3.0.1(d3-selection@3.0.0) - d3-zoom: 3.0.0 - - dagre-d3-es@7.0.14: - dependencies: - d3: 7.9.0 - lodash-es: 4.18.1 - - data-urls@5.0.0: - dependencies: - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - - dayjs@1.11.20: {} - - de-indent@1.0.2: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decamelize@1.2.0: {} - - decimal.js@10.6.0: {} - - decode-named-character-reference@1.3.0: - dependencies: - character-entities: 2.0.2 - - decode-uri-component@0.4.1: {} - - deep-eql@5.0.2: {} - - deep-is@0.1.4: {} - - delaunator@5.1.0: - dependencies: - robust-predicates: 3.0.3 - - delayed-stream@1.0.0: {} - - dequal@2.0.3: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - didyoumean@1.2.2: {} - - dijkstrajs@1.0.3: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dlv@1.1.3: {} - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - dompurify@3.3.1: - optionalDependencies: - '@types/trusted-types': 2.0.7 - - dompurify@3.3.3: - optionalDependencies: - '@types/trusted-types': 2.0.7 - - driver.js@1.4.0: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - eastasianwidth@0.2.0: {} - - editorconfig@1.0.4: - dependencies: - '@one-ini/wasm': 0.1.1 - commander: 10.0.1 - minimatch: 9.0.1 - semver: 7.7.3 - - electron-to-chromium@1.5.267: {} - - emoji-mart@5.6.0: {} - - emoji-regex@10.6.0: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - entities@6.0.1: {} - - entities@7.0.0: {} - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@1.7.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-toolkit@1.45.1: {} - - esast-util-from-estree@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - unist-util-position-from-estree: 2.0.0 - - esast-util-from-js@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - acorn: 8.16.0 - esast-util-from-estree: 2.0.0 - vfile-message: 4.0.3 - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - escalade@3.2.0: {} - - escape-string-regexp@4.0.0: {} - - escape-string-regexp@5.0.0: {} - - eslint-plugin-vue@9.33.0(eslint@8.57.1): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - eslint: 8.57.1 - globals: 13.24.0 - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 6.1.2 - semver: 7.7.3 - vue-eslint-parser: 9.4.3(eslint@8.57.1) - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color - - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint@8.57.1: - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - espree@9.6.1: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - - esquery@1.7.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - estree-util-attach-comments@3.0.0: - dependencies: - '@types/estree': 1.0.8 - - estree-util-build-jsx@3.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - estree-walker: 3.0.3 - - estree-util-is-identifier-name@3.0.0: {} - - estree-util-scope@1.0.0: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - - estree-util-to-js@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - astring: 1.9.0 - source-map: 0.7.6 - - estree-util-visit@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/unist': 3.0.3 - - estree-walker@2.0.2: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - - esutils@2.0.3: {} - - expect-type@1.3.0: {} - - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - - extend-shallow@3.0.2: - dependencies: - assign-symbols: 1.0.0 - is-extendable: 1.0.1 - - extend@3.0.2: {} - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - - file-saver@2.0.5: {} - - file-selector@0.5.0: - dependencies: - tslib: 2.8.1 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - filter-obj@5.1.0: {} - - find-root@1.1.0: {} - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@3.2.0: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.3.3: {} - - follow-redirects@1.16.0: {} - - for-in@1.0.2: {} - - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - frac@1.1.2: {} - - fraction.js@5.3.4: {} - - framer-motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - motion-dom: 12.38.0 - motion-utils: 12.36.0 - tslib: 2.8.1 - optionalDependencies: - '@emotion/is-prop-valid': 1.4.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - fs.realpath@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-caller-file@2.0.5: {} - - get-east-asian-width@1.5.0: {} - - get-intrinsic@1.3.0: - 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 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-value@2.0.6: {} - - giscus@1.6.0: - dependencies: - lit: 3.3.2 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} - - graphemer@1.4.0: {} - - hachure-fill@0.5.2: {} - - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - hast-util-from-dom@5.0.1: - dependencies: - '@types/hast': 3.0.4 - hastscript: 9.0.1 - web-namespaces: 2.0.1 - - hast-util-from-html-isomorphic@2.0.0: - dependencies: - '@types/hast': 3.0.4 - hast-util-from-dom: 5.0.1 - hast-util-from-html: 2.0.3 - unist-util-remove-position: 5.0.0 - - hast-util-from-html@2.0.3: - dependencies: - '@types/hast': 3.0.4 - devlop: 1.1.0 - hast-util-from-parse5: 8.0.3 - parse5: 7.3.0 - vfile: 6.0.3 - vfile-message: 4.0.3 - - hast-util-from-parse5@8.0.3: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - devlop: 1.1.0 - hastscript: 9.0.1 - property-information: 7.1.0 - vfile: 6.0.3 - vfile-location: 5.0.3 - web-namespaces: 2.0.1 - - hast-util-is-element@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - hast-util-parse-selector@4.0.0: - dependencies: - '@types/hast': 3.0.4 - - hast-util-raw@9.1.0: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - '@ungap/structured-clone': 1.3.0 - hast-util-from-parse5: 8.0.3 - hast-util-to-parse5: 8.0.1 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - parse5: 7.3.0 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - - hast-util-to-estree@3.1.3: - dependencies: - '@types/estree': 1.0.8 - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-attach-comments: 3.0.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - style-to-js: 1.1.21 - unist-util-position: 5.0.0 - zwitch: 2.0.4 - transitivePeerDependencies: - - supports-color - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-to-jsx-runtime@2.3.6: - dependencies: - '@types/estree': 1.0.8 - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - style-to-js: 1.1.21 - unist-util-position: 5.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - hast-util-to-parse5@8.0.1: - dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - - hast-util-to-text@4.0.2: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - hast-util-is-element: 3.0.0 - unist-util-find-after: 5.0.0 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - hastscript@9.0.1: - dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - - he@1.2.0: {} - - hoist-non-react-statics@3.3.2: - dependencies: - react-is: 16.13.1 - - html-encoding-sniffer@4.0.0: - dependencies: - whatwg-encoding: 3.1.1 - - html-escaper@2.0.2: {} - - html-url-attributes@3.0.1: {} - - html-void-elements@3.0.0: {} - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ignore@5.3.2: {} - - immer@11.1.4: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - ini@1.3.8: {} - - inline-style-parser@0.2.7: {} - - internmap@1.0.1: {} - - internmap@2.0.3: {} - - intersection-observer@0.12.2: {} - - is-alphabetical@2.0.1: {} - - is-alphanumerical@2.0.1: - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - - is-arrayish@0.2.1: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-decimal@2.0.1: {} - - is-extendable@0.1.1: {} - - is-extendable@1.0.1: - dependencies: - is-plain-object: 2.0.4 - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-hexadecimal@2.0.1: {} - - is-mobile@5.0.0: {} - - is-number@7.0.0: {} - - is-path-inside@3.0.3: {} - - is-plain-obj@4.1.0: {} - - is-plain-object@2.0.4: - dependencies: - isobject: 3.0.1 - - is-potential-custom-element-name@1.0.1: {} - - isexe@2.0.0: {} - - isobject@3.0.1: {} - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jiti@1.21.7: {} - - js-beautify@1.15.4: - dependencies: - config-chain: 1.1.13 - editorconfig: 1.0.4 - glob: 10.5.0 - js-cookie: 3.0.7 - nopt: 7.2.1 - - js-cookie@3.0.7: {} - - js-tokens@4.0.0: {} - - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - jsdom@24.1.3: - dependencies: - cssstyle: 4.6.0 - data-urls: 5.0.0 - decimal.js: 10.6.0 - form-data: 4.0.5 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.23 - parse5: 7.3.0 - rrweb-cssom: 0.7.1 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.4 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - ws: 8.19.0 - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json2mq@0.2.0: - dependencies: - string-convert: 0.2.1 - - katex@0.16.45: - dependencies: - commander: 8.3.0 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - khroma@2.1.0: {} - - langium@4.2.2: - dependencies: - '@chevrotain/regexp-to-ast': 12.0.0 - chevrotain: 12.0.0 - chevrotain-allstar: 0.4.1(chevrotain@12.0.0) - vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 - - layout-base@1.0.2: {} - - layout-base@2.0.1: {} - - leva@0.10.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@radix-ui/react-portal': 1.1.10(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-tooltip': 1.2.8(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@stitches/react': 1.2.8(react@19.2.3) - '@use-gesture/react': 10.3.1(react@19.2.3) - colord: 2.9.3 - dequal: 2.0.3 - merge-value: 1.0.0 - react: 19.2.3 - react-colorful: 5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-dom: 19.2.3(react@19.2.3) - react-dropzone: 12.1.0(react@19.2.3) - v8n: 1.5.1 - zustand: 3.7.2(react@19.2.3) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lilconfig@3.1.3: {} - - lines-and-columns@1.2.4: {} - - lit-element@4.2.2: - dependencies: - '@lit-labs/ssr-dom-shim': 1.5.1 - '@lit/reactive-element': 2.1.2 - lit-html: 3.3.2 - - lit-html@3.3.2: - dependencies: - '@types/trusted-types': 2.0.7 - - lit@3.3.2: - dependencies: - '@lit/reactive-element': 2.1.2 - lit-element: 4.2.2 - lit-html: 3.3.2 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash-es@4.18.1: {} - - lodash.merge@4.6.2: {} - - lodash@4.17.21: {} - - lodash@4.18.1: {} - - longest-streak@3.1.0: {} - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - loupe@3.2.1: {} - - lru-cache@10.4.3: {} - - lucide-react@0.469.0(react@19.2.3): - dependencies: - react: 19.2.3 - - lucide-react@0.562.0(react@19.2.3): - dependencies: - react: 19.2.3 - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - magicast@0.3.5: - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - source-map-js: 1.2.1 - - make-dir@4.0.0: - dependencies: - semver: 7.7.3 - - markdown-extensions@2.0.0: {} - - markdown-table@3.0.4: {} - - marked@16.4.2: {} - - marked@17.0.1: {} - - marked@17.0.6: {} - - math-intrinsics@1.1.0: {} - - mdast-util-find-and-replace@3.0.2: - dependencies: - '@types/mdast': 4.0.4 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - mdast-util-from-markdown@2.0.3: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.2 - micromark-util-character: 2.1.1 - - mdast-util-gfm-footnote@2.1.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - micromark-util-normalize-identifier: 2.0.1 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-strikethrough@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-table@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-task-list-item@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm@3.1.0: - dependencies: - mdast-util-from-markdown: 2.0.3 - mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-math@3.0.0: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - unist-util-remove-position: 5.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-expression@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-jsx@3.2.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx@3.0.0: - dependencies: - mdast-util-from-markdown: 2.0.3 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdxjs-esm@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-newline-to-break@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-find-and-replace: 3.0.2 - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.1 - - mdast-util-to-hast@13.2.1: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - mdast-util-to-markdown@2.1.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.1 - micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.1.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - - merge-value@1.0.0: - dependencies: - get-value: 2.0.6 - is-extendable: 1.0.1 - mixin-deep: 1.3.2 - set-value: 2.0.1 - - merge2@1.4.1: {} - - mermaid@11.14.0: - dependencies: - '@braintree/sanitize-url': 7.1.2 - '@iconify/utils': 3.1.0 - '@mermaid-js/parser': 1.1.0 - '@types/d3': 7.4.3 - '@upsetjs/venn.js': 2.0.0 - cytoscape: 3.33.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.2) - cytoscape-fcose: 2.2.0(cytoscape@3.33.2) - d3: 7.9.0 - d3-sankey: 0.12.3 - dagre-d3-es: 7.0.14 - dayjs: 1.11.20 - dompurify: 3.3.3 - katex: 0.16.45 - khroma: 2.1.0 - lodash-es: 4.18.1 - marked: 16.4.2 - roughjs: 4.6.6 - stylis: 4.3.6 - ts-dedent: 2.2.0 - uuid: 11.1.0 - - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.1 - micromark-factory-label: 2.0.1 - micromark-factory-space: 2.0.1 - micromark-factory-title: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-html-tag-name: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-cjk-friendly-util@2.1.1(micromark-util-types@2.0.2): - dependencies: - get-east-asian-width: 1.5.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - optionalDependencies: - micromark-util-types: 2.0.2 - - micromark-extension-cjk-friendly@1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2): - dependencies: - devlop: 1.1.0 - micromark: 4.0.2 - micromark-extension-cjk-friendly-util: 2.1.1(micromark-util-types@2.0.2) - micromark-util-chunked: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-symbol: 2.0.1 - optionalDependencies: - micromark-util-types: 2.0.2 - - micromark-extension-gfm-autolink-literal@2.1.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-footnote@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-strikethrough@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-table@2.1.1: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-tagfilter@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-gfm-task-list-item@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm@3.0.0: - dependencies: - micromark-extension-gfm-autolink-literal: 2.1.0 - micromark-extension-gfm-footnote: 2.1.0 - micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.1 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.1.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-math@3.1.0: - dependencies: - '@types/katex': 0.16.8 - devlop: 1.1.0 - katex: 0.16.45 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-mdx-expression@3.0.1: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-factory-mdx-expression: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-mdx-jsx@3.0.2: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - micromark-factory-mdx-expression: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - vfile-message: 4.0.3 - - micromark-extension-mdx-md@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-mdxjs-esm@3.0.0: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.3 - - micromark-extension-mdxjs@3.0.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - micromark-extension-mdx-expression: 3.0.1 - micromark-extension-mdx-jsx: 3.0.2 - micromark-extension-mdx-md: 2.0.0 - micromark-extension-mdxjs-esm: 3.0.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-destination@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-label@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-mdx-expression@2.0.3: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.3 - - micromark-factory-space@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - - micromark-factory-title@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-whitespace@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-chunked@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-classify-character@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-combine-extensions@2.0.1: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-decode-numeric-character-reference@2.0.2: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-decode-string@2.0.1: - dependencies: - decode-named-character-reference: 1.3.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-events-to-acorn@2.0.3: - dependencies: - '@types/estree': 1.0.8 - '@types/unist': 3.0.3 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - vfile-message: 4.0.3 - - micromark-util-html-tag-name@2.0.1: {} - - micromark-util-normalize-identifier@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-resolve-all@2.0.1: - dependencies: - micromark-util-types: 2.0.2 - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-subtokenize@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromark@4.0.2: - dependencies: - '@types/debug': 4.1.13 - debug: 4.4.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-combine-extensions: 2.0.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.1: - dependencies: - brace-expansion: 2.0.2 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minipass@7.1.2: {} - - mixin-deep@1.3.2: - dependencies: - for-in: 1.0.2 - is-extendable: 1.0.1 - - mlly@1.8.2: - dependencies: - acorn: 8.16.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.3 - - motion-dom@12.38.0: - dependencies: - motion-utils: 12.36.0 - - motion-utils@12.36.0: {} - - motion@12.23.26(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - framer-motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - tslib: 2.8.1 - optionalDependencies: - '@emotion/is-prop-valid': 1.4.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - ms@2.1.3: {} - - muggle-string@0.4.1: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.11: {} - - natural-compare@1.4.0: {} - - node-releases@2.0.27: {} - - nopt@7.2.1: - dependencies: - abbrev: 2.0.0 - - normalize-path@3.0.0: {} - - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - numeral@2.0.6: {} - - nwsapi@2.2.23: {} - - object-assign@4.1.1: {} - - object-hash@3.0.0: {} - - on-change@4.0.2: {} - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - oniguruma-parser@0.12.1: {} - - oniguruma-to-es@4.3.5: - dependencies: - oniguruma-parser: 0.12.1 - regex: 6.1.0 - regex-recursion: 6.0.2 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-try@2.2.0: {} - - package-json-from-dist@1.0.1: {} - - package-manager-detector@1.6.0: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-entities@4.0.2: - dependencies: - '@types/unist': 2.0.11 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.3.0 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parse5@7.3.0: - dependencies: - entities: 6.0.1 - - path-browserify@1.0.1: {} - - path-data-parser@0.1.0: {} - - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - path-parse@1.0.7: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-type@4.0.0: {} - - pathe@1.1.2: {} - - pathe@2.0.3: {} - - pathval@2.0.1: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - pify@2.3.0: {} - - pinia@2.3.1(typescript@5.6.3)(vue@3.5.26(typescript@5.6.3)): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.5.26(typescript@5.6.3) - vue-demi: 0.14.10(vue@3.5.26(typescript@5.6.3)) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - '@vue/composition-api' - - pirates@4.0.7: {} - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.2 - pathe: 2.0.3 - - pngjs@5.0.0: {} - - points-on-curve@0.2.0: {} - - points-on-path@0.2.1: - dependencies: - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - - polished@4.3.1: - dependencies: - '@babel/runtime': 7.28.4 - - postcss-import@15.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.11 - - postcss-js@4.1.0(postcss@8.5.6): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.5.6 - - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6): - dependencies: - lilconfig: 3.1.3 - optionalDependencies: - jiti: 1.21.7 - postcss: 8.5.6 - - postcss-nested@6.2.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-value-parser@4.2.0: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - property-information@7.1.0: {} - - proto-list@1.2.4: {} - - proxy-from-env@2.1.0: {} - - psl@1.15.0: - dependencies: - punycode: 2.3.1 - - punycode@2.3.1: {} - - qrcode@1.5.4: - dependencies: - dijkstrajs: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 - - query-string@9.3.1: - dependencies: - decode-uri-component: 0.4.1 - filter-obj: 5.1.0 - split-on-first: 3.0.0 - - querystringify@2.2.0: {} - - queue-microtask@1.2.3: {} - - rc-collapse@4.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-dialog@9.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-footer@0.6.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-image@7.12.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/portal': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-dialog: 9.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-input-number@9.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/mini-decimal': 1.1.3 - classnames: 2.5.1 - rc-input: 1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-input@1.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-menu@9.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - '@rc-component/trigger': 2.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-overflow: 1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-motion@2.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-overflow@1.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - rc-resize-observer@1.4.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - resize-observer-polyfill: 1.5.1 - - rc-util@5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - '@babel/runtime': 7.29.2 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-is: 18.3.1 - - re-resizable@6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - react-avatar-editor@14.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - react-colorful@5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - react-dom@19.2.3(react@19.2.3): - dependencies: - react: 19.2.3 - scheduler: 0.27.0 - - react-draggable@4.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - clsx: 2.1.1 - prop-types: 15.8.1 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - react-dropzone@12.1.0(react@19.2.3): - dependencies: - attr-accept: 2.2.5 - file-selector: 0.5.0 - prop-types: 15.8.1 - react: 19.2.3 - - react-error-boundary@6.1.1(react@19.2.3): - dependencies: - react: 19.2.3 - - react-fast-compare@3.2.2: {} - - react-hotkeys-hook@5.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - react-is@16.13.1: {} - - react-is@18.3.1: {} - - react-markdown@10.1.0(@types/react@19.2.7)(react@19.2.3): - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/react': 19.2.7 - devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6 - html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.1 - react: 19.2.3 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - unified: 11.0.5 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - - react-merge-refs@3.0.2(react@19.2.3): - optionalDependencies: - react: 19.2.3 - - react-rnd@10.5.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - re-resizable: 6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-draggable: 4.5.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - tslib: 2.6.2 - - react-zoom-pan-pinch@3.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - react@19.2.3: {} - - read-cache@1.0.0: - dependencies: - pify: 2.3.0 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - readdirp@4.1.2: {} - - recma-build-jsx@1.0.0: - dependencies: - '@types/estree': 1.0.8 - estree-util-build-jsx: 3.0.1 - vfile: 6.0.3 - - recma-jsx@1.0.1(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - estree-util-to-js: 2.0.0 - recma-parse: 1.0.0 - recma-stringify: 1.0.0 - unified: 11.0.5 - - recma-parse@1.0.0: - dependencies: - '@types/estree': 1.0.8 - esast-util-from-js: 2.0.1 - unified: 11.0.5 - vfile: 6.0.3 - - recma-stringify@1.0.0: - dependencies: - '@types/estree': 1.0.8 - estree-util-to-js: 2.0.0 - unified: 11.0.5 - vfile: 6.0.3 - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.1.0: - dependencies: - regex-utilities: 2.3.0 - - rehype-github-alerts@4.2.0: - dependencies: - '@primer/octicons': 19.23.1 - hast-util-from-html: 2.0.3 - hast-util-is-element: 3.0.0 - unist-util-visit: 5.1.0 - - rehype-katex@7.0.1: - dependencies: - '@types/hast': 3.0.4 - '@types/katex': 0.16.8 - hast-util-from-html-isomorphic: 2.0.0 - hast-util-to-text: 4.0.2 - katex: 0.16.45 - unist-util-visit-parents: 6.0.2 - vfile: 6.0.3 - - rehype-raw@7.0.0: - dependencies: - '@types/hast': 3.0.4 - hast-util-raw: 9.1.0 - vfile: 6.0.3 - - rehype-recma@1.0.0: - dependencies: - '@types/estree': 1.0.8 - '@types/hast': 3.0.4 - hast-util-to-estree: 3.1.3 - transitivePeerDependencies: - - supports-color - - remark-breaks@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-newline-to-break: 2.0.0 - unified: 11.0.5 - - remark-cjk-friendly@1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5): - dependencies: - micromark-extension-cjk-friendly: 1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2) - unified: 11.0.5 - optionalDependencies: - '@types/mdast': 4.0.4 - transitivePeerDependencies: - - micromark - - micromark-util-types - - remark-gfm@4.0.1: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-gfm: 3.1.0 - micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 - remark-stringify: 11.0.0 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-github@12.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-find-and-replace: 3.0.2 - mdast-util-to-string: 4.0.0 - to-vfile: 8.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - remark-math@6.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-math: 3.0.0 - micromark-extension-math: 3.1.0 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-mdx@3.1.1: - dependencies: - mdast-util-mdx: 3.0.0 - micromark-extension-mdxjs: 3.0.0 - transitivePeerDependencies: - - supports-color - - remark-parse@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 - micromark-util-types: 2.0.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - remark-rehype@11.1.2: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.1 - unified: 11.0.5 - vfile: 6.0.3 - - remark-stringify@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.2 - unified: 11.0.5 - - require-directory@2.1.1: {} - - require-main-filename@2.0.0: {} - - requires-port@1.0.0: {} - - reselect@5.1.1: {} - - resize-observer-polyfill@1.5.1: {} - - resolve-from@4.0.0: {} - - resolve@1.22.11: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.1.0: {} - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - - robust-predicates@3.0.3: {} - - rollup@4.54.0: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.54.0 - '@rollup/rollup-android-arm64': 4.54.0 - '@rollup/rollup-darwin-arm64': 4.54.0 - '@rollup/rollup-darwin-x64': 4.54.0 - '@rollup/rollup-freebsd-arm64': 4.54.0 - '@rollup/rollup-freebsd-x64': 4.54.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 - '@rollup/rollup-linux-arm-musleabihf': 4.54.0 - '@rollup/rollup-linux-arm64-gnu': 4.54.0 - '@rollup/rollup-linux-arm64-musl': 4.54.0 - '@rollup/rollup-linux-loong64-gnu': 4.54.0 - '@rollup/rollup-linux-ppc64-gnu': 4.54.0 - '@rollup/rollup-linux-riscv64-gnu': 4.54.0 - '@rollup/rollup-linux-riscv64-musl': 4.54.0 - '@rollup/rollup-linux-s390x-gnu': 4.54.0 - '@rollup/rollup-linux-x64-gnu': 4.54.0 - '@rollup/rollup-linux-x64-musl': 4.54.0 - '@rollup/rollup-openharmony-arm64': 4.54.0 - '@rollup/rollup-win32-arm64-msvc': 4.54.0 - '@rollup/rollup-win32-ia32-msvc': 4.54.0 - '@rollup/rollup-win32-x64-gnu': 4.54.0 - '@rollup/rollup-win32-x64-msvc': 4.54.0 - fsevents: 2.3.3 - - roughjs@4.6.6: - dependencies: - hachure-fill: 0.5.2 - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - points-on-path: 0.2.1 - - rrweb-cssom@0.7.1: {} - - rrweb-cssom@0.8.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rw@1.3.3: {} - - safer-buffer@2.1.2: {} - - saxes@6.0.0: - dependencies: - xmlchars: 2.2.0 - - scheduler@0.27.0: {} - - screenfull@5.2.0: {} - - scroll-into-view-if-needed@3.1.0: - dependencies: - compute-scroll-into-view: 3.1.1 - - semver-compare@1.0.0: {} - - semver@7.7.3: {} - - set-blocking@2.0.0: {} - - set-value@2.0.1: - dependencies: - extend-shallow: 2.0.1 - is-extendable: 0.1.1 - is-plain-object: 2.0.4 - split-string: 3.1.0 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shiki-stream@0.1.4(react@19.2.3)(vue@3.5.26(typescript@5.6.3)): - dependencies: - '@shikijs/core': 3.23.0 - optionalDependencies: - react: 19.2.3 - vue: 3.5.26(typescript@5.6.3) - - shiki@3.23.0: - dependencies: - '@shikijs/core': 3.23.0 - '@shikijs/engine-javascript': 3.23.0 - '@shikijs/engine-oniguruma': 3.23.0 - '@shikijs/langs': 3.23.0 - '@shikijs/themes': 3.23.0 - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} - - slash@3.0.0: {} - - source-map-js@1.2.1: {} - - source-map@0.5.7: {} - - source-map@0.7.6: {} - - space-separated-tokens@2.0.2: {} - - split-on-first@3.0.0: {} - - split-string@3.1.0: - dependencies: - extend-shallow: 3.0.2 - - ssf@0.11.2: - dependencies: - frac: 1.1.2 - - stackback@0.0.2: {} - - std-env@3.10.0: {} - - string-convert@0.2.1: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.2 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - - strip-json-comments@3.1.1: {} - - style-to-js@1.1.21: - dependencies: - style-to-object: 1.0.14 - - style-to-object@1.0.14: - dependencies: - inline-style-parser: 0.2.7 - - stylis@4.2.0: {} - - stylis@4.3.6: {} - - sucrase@3.35.1: - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - commander: 4.1.1 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.7 - tinyglobby: 0.2.15 - ts-interface-checker: 0.1.13 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - swr@2.4.1(react@19.2.3): - dependencies: - dequal: 2.0.3 - react: 19.2.3 - use-sync-external-store: 1.6.0(react@19.2.3) - - symbol-tree@3.2.4: {} - - tabbable@6.4.0: {} - - tailwindcss@3.4.19: - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.3 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.7 - lilconfig: 3.1.3 - micromatch: 4.0.8 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-import: 15.1.0(postcss@8.5.6) - postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6) - postcss-nested: 6.2.0(postcss@8.5.6) - postcss-selector-parser: 6.1.2 - resolve: 1.22.11 - sucrase: 3.35.1 - transitivePeerDependencies: - - tsx - - yaml - - test-exclude@7.0.1: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 10.5.0 - minimatch: 9.0.5 - - text-table@0.2.0: {} - - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - throttle-debounce@5.0.2: {} - - tiny-invariant@1.3.3: {} - - tinybench@2.9.0: {} - - tinyexec@0.3.2: {} - - tinyexec@1.1.1: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tinypool@1.1.1: {} - - tinyrainbow@1.2.0: {} - - tinyspy@3.0.2: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - to-vfile@8.0.0: - dependencies: - vfile: 6.0.3 - - tough-cookie@4.1.4: - dependencies: - psl: 1.15.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 - - tr46@5.1.1: - dependencies: - punycode: 2.3.1 - - trim-lines@3.0.1: {} - - trough@2.2.0: {} - - ts-api-utils@1.4.3(typescript@5.6.3): - dependencies: - typescript: 5.6.3 - - ts-dedent@2.2.0: {} - - ts-interface-checker@0.1.13: {} - - ts-md5@2.0.1: {} - - tslib@2.6.2: {} - - tslib@2.8.1: {} - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.20.2: {} - - typescript@5.6.3: {} - - ufo@1.6.3: {} - - undici-types@6.21.0: {} - - unicorn-magic@0.3.0: {} - - unified@11.0.5: - dependencies: - '@types/unist': 3.0.3 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.3 - - unist-util-find-after@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position-from-estree@2.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-remove-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-visit: 5.1.0 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - universalify@0.2.0: {} - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - url-join@5.0.0: {} - - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - - use-merge-value@1.2.0(react@19.2.3): - dependencies: - react: 19.2.3 - - use-sync-external-store@1.6.0(react@19.2.3): - dependencies: - react: 19.2.3 - - util-deprecate@1.0.2: {} - - uuid@11.1.0: {} - - uuid@13.0.0: {} - - v8n@1.5.1: {} - - vfile-location@5.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile: 6.0.3 - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - - vite-node@2.1.9(@types/node@20.19.27): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 1.1.2 - vite: 5.4.21(@types/node@20.19.27) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite-plugin-checker@0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.6.3)(vite@5.4.21(@types/node@20.19.27))(vue-tsc@2.2.12(typescript@5.6.3)): - dependencies: - '@babel/code-frame': 7.27.1 - chokidar: 4.0.3 - npm-run-path: 6.0.0 - picocolors: 1.1.1 - picomatch: 4.0.3 - strip-ansi: 7.1.2 - tiny-invariant: 1.3.3 - tinyglobby: 0.2.15 - vite: 5.4.21(@types/node@20.19.27) - vscode-uri: 3.1.0 - optionalDependencies: - eslint: 8.57.1 - optionator: 0.9.4 - typescript: 5.6.3 - vue-tsc: 2.2.12(typescript@5.6.3) - - vite@5.4.21(@types/node@20.19.27): - dependencies: - esbuild: 0.21.5 - postcss: 8.5.6 - rollup: 4.54.0 - optionalDependencies: - '@types/node': 20.19.27 - fsevents: 2.3.3 - - vitest@2.1.9(@types/node@20.19.27)(jsdom@24.1.3): - dependencies: - '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.27)) - '@vitest/pretty-format': 2.1.9 - '@vitest/runner': 2.1.9 - '@vitest/snapshot': 2.1.9 - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 1.1.2 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinypool: 1.1.1 - tinyrainbow: 1.2.0 - vite: 5.4.21(@types/node@20.19.27) - vite-node: 2.1.9(@types/node@20.19.27) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 20.19.27 - jsdom: 24.1.3 - transitivePeerDependencies: - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vscode-jsonrpc@8.2.0: {} - - vscode-languageserver-protocol@3.17.5: - dependencies: - vscode-jsonrpc: 8.2.0 - vscode-languageserver-types: 3.17.5 - - vscode-languageserver-textdocument@1.0.12: {} - - vscode-languageserver-types@3.17.5: {} - - vscode-languageserver@9.0.1: - dependencies: - vscode-languageserver-protocol: 3.17.5 - - vscode-uri@3.1.0: {} - - vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.26(typescript@5.6.3)): - dependencies: - chart.js: 4.5.1 - vue: 3.5.26(typescript@5.6.3) - - vue-component-type-helpers@2.2.12: {} - - vue-demi@0.14.10(vue@3.5.26(typescript@5.6.3)): - dependencies: - vue: 3.5.26(typescript@5.6.3) - - vue-draggable-plus@0.6.1(@types/sortablejs@1.15.9): - dependencies: - '@types/sortablejs': 1.15.9 - - vue-eslint-parser@9.4.3(eslint@8.57.1): - dependencies: - debug: 4.4.3 - eslint: 8.57.1 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.7.0 - lodash: 4.17.21 - semver: 7.7.3 - transitivePeerDependencies: - - supports-color - - vue-i18n@9.14.5(vue@3.5.26(typescript@5.6.3)): - dependencies: - '@intlify/core-base': 9.14.5 - '@intlify/shared': 9.14.5 - '@vue/devtools-api': 6.6.4 - vue: 3.5.26(typescript@5.6.3) - - vue-router@4.6.4(vue@3.5.26(typescript@5.6.3)): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.5.26(typescript@5.6.3) - - vue-tsc@2.2.12(typescript@5.6.3): - dependencies: - '@volar/typescript': 2.4.15 - '@vue/language-core': 2.2.12(typescript@5.6.3) - typescript: 5.6.3 - - vue@3.5.26(typescript@5.6.3): - dependencies: - '@vue/compiler-dom': 3.5.26 - '@vue/compiler-sfc': 3.5.26 - '@vue/runtime-dom': 3.5.26 - '@vue/server-renderer': 3.5.26(vue@3.5.26(typescript@5.6.3)) - '@vue/shared': 3.5.26 - optionalDependencies: - typescript: 5.6.3 - - w3c-xmlserializer@5.0.0: - dependencies: - xml-name-validator: 5.0.0 - - web-namespaces@2.0.1: {} - - webidl-conversions@7.0.0: {} - - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@4.0.0: {} - - whatwg-url@14.2.0: - dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 - - which-module@2.0.1: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - wmf@1.0.2: {} - - word-wrap@1.2.5: {} - - word@0.3.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.1.2 - - wrappy@1.0.2: {} - - ws@8.19.0: {} - - xlsx@0.18.5: - dependencies: - adler-32: 1.3.1 - cfb: 1.2.2 - codepage: 1.15.0 - crc-32: 1.2.2 - ssf: 0.11.2 - wmf: 1.0.2 - word: 0.3.0 - - xml-name-validator@4.0.0: {} - - xml-name-validator@5.0.0: {} - - xmlchars@2.2.0: {} - - y18n@4.0.3: {} - - yaml@1.10.2: {} - - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - - yocto-queue@0.1.0: {} - - zustand@3.7.2(react@19.2.3): - optionalDependencies: - react: 19.2.3 - - zwitch@2.0.4: {} diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7485aa1afcf..ece3bf76802 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -3,9 +3,10 @@ import { RouterView, useRouter, useRoute } from 'vue-router' import { onMounted, onBeforeUnmount, watch } from 'vue' import Toast from '@/components/common/Toast.vue' import NavigationProgress from '@/components/common/NavigationProgress.vue' +import AdminComplianceDialog from '@/components/admin/AdminComplianceDialog.vue' import { resolveDocumentTitle } from '@/router/title' import AnnouncementPopup from '@/components/common/AnnouncementPopup.vue' -import { useAppStore, useAuthStore, useSubscriptionStore, useAnnouncementStore } from '@/stores' +import { useAppStore, useAuthStore, useSubscriptionStore, useAnnouncementStore, useAdminComplianceStore } from '@/stores' import { getSetupStatus } from '@/api/setup' const router = useRouter() @@ -14,6 +15,7 @@ const appStore = useAppStore() const authStore = useAuthStore() const subscriptionStore = useSubscriptionStore() const announcementStore = useAnnouncementStore() +const adminComplianceStore = useAdminComplianceStore() /** * Update favicon dynamically @@ -49,10 +51,21 @@ function onVisibilityChange() { } } +function onAdminComplianceRequired(event: Event) { + const detail = (event as CustomEvent>).detail || {} + adminComplianceStore.requireAcknowledgement(detail) +} + watch( () => authStore.isAuthenticated, (isAuthenticated, oldValue) => { if (isAuthenticated) { + if (authStore.isAdmin) { + adminComplianceStore.fetchStatus().catch((error) => { + console.error('Failed to fetch admin compliance status:', error) + }) + } + // User logged in: preload subscriptions and start polling subscriptionStore.fetchActiveSubscriptions().catch((error) => { console.error('Failed to preload subscriptions:', error) @@ -74,6 +87,7 @@ watch( // User logged out: clear data and stop polling subscriptionStore.clear() announcementStore.reset() + adminComplianceStore.reset() document.removeEventListener('visibilitychange', onVisibilityChange) } }, @@ -89,9 +103,12 @@ router.afterEach(() => { onBeforeUnmount(() => { document.removeEventListener('visibilitychange', onVisibilityChange) + window.removeEventListener('admin-compliance-required', onAdminComplianceRequired) }) onMounted(async () => { + window.addEventListener('admin-compliance-required', onAdminComplianceRequired) + // Check if setup is needed try { const status = await getSetupStatus() @@ -116,4 +133,5 @@ onMounted(async () => { + diff --git a/frontend/src/api/__tests__/client.spec.ts b/frontend/src/api/__tests__/client.spec.ts index a46c39eb46f..8745df02928 100644 --- a/frontend/src/api/__tests__/client.spec.ts +++ b/frontend/src/api/__tests__/client.spec.ts @@ -143,6 +143,53 @@ describe('API Client', () => { }) ) }) + + it('部署与运营合规未确认时广播事件且保留登录态', async () => { + localStorage.setItem('auth_token', 'admin-token') + const listener = vi.fn() + window.addEventListener('admin-compliance-required', listener) + + const adapter = vi.fn().mockRejectedValue({ + response: { + status: 423, + data: { + code: 'ADMIN_COMPLIANCE_ACK_REQUIRED', + message: 'administrator compliance acknowledgement is required', + metadata: { + version: 'v2026.06.10', + document_path_zh: 'docs/legal/admin-compliance.zh.md', + document_path_en: 'docs/legal/admin-compliance.en.md', + }, + }, + }, + config: { + url: '/admin/users', + headers: { Authorization: 'Bearer admin-token' }, + }, + code: 'ERR_BAD_REQUEST', + }) + apiClient.defaults.adapter = adapter + + await expect(apiClient.get('/admin/users')).rejects.toEqual( + expect.objectContaining({ + status: 423, + code: 'ADMIN_COMPLIANCE_ACK_REQUIRED', + metadata: expect.objectContaining({ + version: 'v2026.06.10', + }), + }) + ) + + expect(listener).toHaveBeenCalledTimes(1) + expect((listener.mock.calls[0][0] as CustomEvent).detail).toEqual( + expect.objectContaining({ + version: 'v2026.06.10', + }) + ) + expect(localStorage.getItem('auth_token')).toBe('admin-token') + + window.removeEventListener('admin-compliance-required', listener) + }) }) // --- 401 Token 刷新 --- diff --git a/frontend/src/api/admin/compliance.ts b/frontend/src/api/admin/compliance.ts new file mode 100644 index 00000000000..0e0c89e918f --- /dev/null +++ b/frontend/src/api/admin/compliance.ts @@ -0,0 +1,42 @@ +import { apiClient } from '@/api/client' + +export interface AdminComplianceAcknowledgement { + version: string + document_zh: string + document_en: string + admin_user_id: number + ip_address?: string + user_agent?: string + accepted_at: string +} + +export interface AdminComplianceStatus { + required: boolean + version: string + document_path_zh: string + document_path_en: string + document_url_zh: string + document_url_en: string + ack_phrase_zh: string + ack_phrase_en: string + acknowledgement?: AdminComplianceAcknowledgement +} + +export interface AcceptAdminComplianceRequest { + phrase: string + language: string +} + +export const adminComplianceAPI = { + async getStatus(): Promise { + const { data } = await apiClient.get('/admin/compliance') + return data + }, + + async accept(payload: AcceptAdminComplianceRequest): Promise { + const { data } = await apiClient.post('/admin/compliance/accept', payload) + return data + } +} + +export default adminComplianceAPI diff --git a/frontend/src/api/admin/groups.ts b/frontend/src/api/admin/groups.ts index b7846efda06..31a1b57650b 100644 --- a/frontend/src/api/admin/groups.ts +++ b/frontend/src/api/admin/groups.ts @@ -57,6 +57,17 @@ export async function getAll(platform?: GroupPlatform): Promise { return data } +/** + * Get ALL groups including disabled ones — used by the API Key group filter so + * that admins can filter users whose keys are still bound to a now-disabled group. + */ +export async function getAllIncludingInactive(): Promise { + const { data } = await apiClient.get('/admin/groups/all', { + params: { include_inactive: true } + }) + return data +} + /** * Get active groups by platform * @param platform - Platform to filter by @@ -322,6 +333,7 @@ export const groupsAPI = { list, getAll, getByPlatform, + getAllIncludingInactive, getById, getModelsListCandidates, create, diff --git a/frontend/src/api/admin/index.ts b/frontend/src/api/admin/index.ts index 384e3796da4..176498a2874 100644 --- a/frontend/src/api/admin/index.ts +++ b/frontend/src/api/admin/index.ts @@ -31,6 +31,7 @@ import channelMonitorTemplateAPI from './channelMonitorTemplate' import adminPaymentAPI from './payment' import affiliatesAPI from './affiliates' import riskControlAPI from './riskControl' +import adminComplianceAPI from './compliance' /** * Unified admin API object for convenient access @@ -63,7 +64,8 @@ export const adminAPI = { channelMonitorTemplate: channelMonitorTemplateAPI, payment: adminPaymentAPI, affiliates: affiliatesAPI, - riskControl: riskControlAPI + riskControl: riskControlAPI, + compliance: adminComplianceAPI } export { @@ -94,7 +96,8 @@ export { channelMonitorTemplateAPI, adminPaymentAPI, affiliatesAPI, - riskControlAPI + riskControlAPI, + adminComplianceAPI } export default adminAPI diff --git a/frontend/src/api/admin/users.ts b/frontend/src/api/admin/users.ts index 61879d2f55e..7667dfc4c93 100644 --- a/frontend/src/api/admin/users.ts +++ b/frontend/src/api/admin/users.ts @@ -60,6 +60,7 @@ export async function list( role?: 'admin' | 'user' search?: string group_name?: string // fuzzy filter by allowed group name + api_key_group_id?: number // filter users by the group their API keys are bound to attributes?: Record // attributeId -> value include_subscriptions?: boolean sort_by?: string @@ -77,6 +78,7 @@ export async function list( role: filters?.role, search: filters?.search, group_name: filters?.group_name, + api_key_group_id: filters?.api_key_group_id, include_subscriptions: filters?.include_subscriptions, sort_by: filters?.sort_by, sort_order: filters?.sort_order diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 54ea4520097..1ba3c0676ac 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -148,6 +148,23 @@ apiClient.interceptors.response.use( }) } + if (status === 423 && apiData.code === 'ADMIN_COMPLIANCE_ACK_REQUIRED') { + try { + window.dispatchEvent(new CustomEvent('admin-compliance-required', { + detail: apiData.metadata || {} + })) + } catch { + // ignore event failures + } + + return Promise.reject({ + status, + code: apiData.code, + message: apiData.message || error.message, + metadata: apiData.metadata, + }) + } + // 401: Try to refresh the token if we have a refresh token // This handles TOKEN_EXPIRED, INVALID_TOKEN, TOKEN_REVOKED, etc. if (status === 401 && !originalRequest._retry) { diff --git a/frontend/src/components/account/AccountStatusIndicator.vue b/frontend/src/components/account/AccountStatusIndicator.vue index a37b5c8e549..8d019bcf7f5 100644 --- a/frontend/src/components/account/AccountStatusIndicator.vue +++ b/frontend/src/components/account/AccountStatusIndicator.vue @@ -220,6 +220,7 @@ const activeModelStatuses = computed(() => { const formatScopeName = (scope: string): string => { const aliases: Record = { // Claude 系列 + 'claude-fable-5': 'CFable5', 'claude-opus-4-6': 'COpus46', 'claude-opus-4-6-thinking': 'COpus46T', 'claude-opus-4-7': 'COpus47', diff --git a/frontend/src/components/account/AccountUsageCell.vue b/frontend/src/components/account/AccountUsageCell.vue index 887fbf7955c..561f90d0e0d 100644 --- a/frontend/src/components/account/AccountUsageCell.vue +++ b/frontend/src/components/account/AccountUsageCell.vue @@ -662,6 +662,7 @@ const antigravity3ImageUsageFromAPI = computed(() => // Claude from API (all Claude model variants) const antigravityClaudeUsageFromAPI = computed(() => getAntigravityUsageFromAPI([ + 'claude-fable-5', 'claude-sonnet-4-5', 'claude-opus-4-5-thinking', 'claude-sonnet-4-6', 'claude-opus-4-6', 'claude-opus-4-6-thinking', 'claude-opus-4-7', 'claude-opus-4-8', diff --git a/frontend/src/components/admin/AdminComplianceDialog.vue b/frontend/src/components/admin/AdminComplianceDialog.vue new file mode 100644 index 00000000000..998202044fc --- /dev/null +++ b/frontend/src/components/admin/AdminComplianceDialog.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/frontend/src/components/common/BaseDialog.vue b/frontend/src/components/common/BaseDialog.vue index 93e4ba36762..6d9a08caa29 100644 --- a/frontend/src/components/common/BaseDialog.vue +++ b/frontend/src/components/common/BaseDialog.vue @@ -18,6 +18,7 @@ {{ title }}