diff --git a/.cursor/rules/main.mdc b/.cursor/rules/main.mdc deleted file mode 100644 index 0014815..0000000 --- a/.cursor/rules/main.mdc +++ /dev/null @@ -1,578 +0,0 @@ ---- -description: 使用 WXT、React 和 TypeScript 构建代理管理扩展的指南 -globs: -alwaysApply: false ---- ---- -description: 使用 WXT、React 和 TypeScript 构建代理管理扩展的指南 -globs: "**/*.ts, **/*.tsx, **/*.js, **/*.jsx" ---- - -## 概览 - -](https://wxt.dev/) 是一个为浏览器扩展开发提供现代开发体验的框架。本指南将帮助您使用 WXT、React 和 TypeScript 重构类似 SwitchyOmega 的代理管理扩展。 - -## 项目结构 - -推荐使用以下项目结构: -``` -. -├── .output/ -├── .wxt/ -├── modules/ -├── public/ # 包含要复制到输出文件夹的任何文件,而无需WXT处理 -├── ord/ # 需要重构的旧代码 -├── src/ -│ │ ├── assets/ -│ │ ├── components/ -│ │ ├── composables/ -│ │ ├── entrypoints/ # 包含所有被捆绑到扩展名的入口点 -│ │ ├── hooks/ # 默认自动导入,包含项目用于 React 和 Solid 的钩子的源代码 -│ │ ├── utils/ -├── .env -├── .env.publish -├── app.config.ts -├── package.json -├── tsconfig.json -├── web-ext.config.ts # 配置浏览器启动 -├── wxt.config.ts # WXT项目的主要配置文件 - -``` - - -Different browsers provide different global variables for accessing the extension APIs (chrome provides chrome, firefox provides browser, etc). - -WXT merges these two into a unified API accessed through the browser variable. - -``` -import { browser } from 'wxt/browser'; - -browser.action.onClicked.addListener(() => { -// ... -}); -``` -TIP - -With auto-imports enabled, you don't even need to import this variable from wxt/browser! - -The browser variable WXT provides is a simple export of the browser or chrome globals provided by the browser at runtime: - - -export const browser = globalThis.browser?.runtime?.id -? globalThis.browser -: globalThis.chrome; -This means you can use the promise-style API for both MV2 and MV3, and it will work across all browsers (Chromium, Firefox, Safari, etc). - -Accessing Types -All types can be accessed via WXT's Browser namespace: - -``` -import { type Browser } from 'wxt/browser'; - -function handleMessage(message: any, sender: Browser.runtime.MessageSender) { -// ... -} -``` - -## 入口点设置 - -### 后台脚本 - -```typescript -// entrypoints/background/index.ts -import { defineBackground } from 'wxt/background'; -import { setupProxyManagement } from './proxy'; - -export default defineBackground({ - // 设置清单选项 - type: 'module', - - main() { - // 初始化代理管理 - setupProxyManagement(); - - // 监听消息 - browser.runtime.onMessage.addListener((message, sender) => { - if (message.type === 'SWITCH_PROXY') { - return handleProxySwitch(message.proxyId); - } - }); - }, -}); -``` - -### 弹出窗口 - -```html - - - - - - - 代理切换器 - - - -
- - - -``` - -```tsx -// entrypoints/popup/index.tsx -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import App from './App'; - -const root = createRoot(document.getElementById('app')!); -root.render(); -``` - -```tsx -// entrypoints/popup/App.tsx -import React, { useState, useEffect } from 'react'; -import ProxySelector from '../../components/ProxySelector'; -import { getProxyList, getCurrentProxy } from '../../utils/proxy'; -import type { Proxy } from '../../types'; - -const App: React.FC = () => { - const [proxies, setProxies] = useState([]); - const [currentProxy, setCurrentProxy] = useState(null); - - useEffect(() => { - const loadData = async () => { - const proxyList = await getProxyList(); - const current = await getCurrentProxy(); - setProxies(proxyList); - setCurrentProxy(current); - }; - - loadData(); - }, []); - - const handleProxyChange = async (proxyId: string) => { - await browser.runtime.sendMessage({ type: 'SWITCH_PROXY', proxyId }); - setCurrentProxy(proxyId); - }; - - return ( -
-

代理切换器

- -
- ); -}; - -export default App; -``` - -### 选项页面 - -```html - - - - - - - 代理切换器设置 - - - -
- - - -``` - -```tsx -// entrypoints/options/App.tsx -import React, { useState, useEffect } from 'react'; -import { getProxyList, saveProxy, deleteProxy } from '../../utils/proxy'; -import type { Proxy } from '../../types'; - -const App: React.FC = () => { - const [proxies, setProxies] = useState([]); - const [newProxy, setNewProxy] = useState>({ - name: '', - host: '', - port: '', - protocol: 'http' - }); - - useEffect(() => { - loadProxies(); - }, []); - - const loadProxies = async () => { - const list = await getProxyList(); - setProxies(list); - }; - - const handleSaveProxy = async () => { - if (!newProxy.name || !newProxy.host || !newProxy.port) return; - - await saveProxy(newProxy as Proxy); - loadProxies(); - setNewProxy({ - name: '', - host: '', - port: '', - protocol: 'http' - }); - }; - - return ( -
-

代理管理器设置

- -
- {proxies.map(proxy => ( -
- {proxy.name} ({proxy.protocol}://{proxy.host}:{proxy.port}) - -
- ))} -
- -
-

添加新代理

- setNewProxy({...newProxy, name: e.target.value})} - /> - - setNewProxy({...newProxy, host: e.target.value})} - /> - setNewProxy({...newProxy, port: e.target.value})} - /> - -
-
- ); -}; - -export default App; -``` - -### 内容脚本 - -```typescript -// entrypoints/content.ts -import { defineContentScript } from 'wxt/content-script'; - -export default defineContentScript({ - matches: [''], - - main() { - // 在页面中执行的内容脚本逻辑 - console.log('代理切换器内容脚本已加载'); - - // 根据需要与后台脚本通信 - browser.runtime.sendMessage({ type: 'CONTENT_SCRIPT_LOADED' }); - }, -}); -``` - -## 最佳实践 - -1. **使用 WXT 存储模块**: 利用 `@wxt-dev/storage` 管理扩展数据。 - - ```typescript - // 安装: npm install @wxt-dev/storage - - // utils/storage.ts - import { createStorage } from '@wxt-dev/storage'; - - export const storage = createStorage({ - proxies: { - defaultValue: [], - schema: z.array(z.object({ - id: z.string(), - name: z.string(), - protocol: z.enum(['http', 'https', 'socks4', 'socks5']), - host: z.string(), - port: z.string() - })) - }, - currentProxyId: { - defaultValue: null, - schema: z.string().nullable() - } - }); - ``` - -2. **组件化开发**: 创建可重用的React组件。 - - ```tsx - // components/ProxySelector.tsx - import React from 'react'; - import type { Proxy } from '../types'; - - interface ProxySelectorProps { - proxies: Proxy[]; - currentProxy: string | null; - onChange: (proxyId: string) => void; - } - - const ProxySelector: React.FC = ({ proxies, currentProxy, onChange }) => { - return ( -
- {proxies.map(proxy => ( -
onChange(proxy.id)} - > - {proxy.name} -
- ))} -
- ); - }; - - export default ProxySelector; - ``` - -3. **类型安全**: 为所有对象定义TypeScript接口。 - - ```typescript - // types/index.ts - export interface Proxy { - id: string; - name: string; - protocol: 'http' | 'https' | 'socks4' | 'socks5'; - host: string; - port: string; - username?: string; - password?: string; - } - - export interface ProxyRule { - id: string; - name: string; - pattern: string; - proxyId: string; - } - ``` - -4. **使用环境变量**: 为不同环境配置不同的设置。 - - ```typescript - // wxt.config.ts - import { defineConfig } from 'wxt'; - - export default defineConfig({ - manifest: { - name: process.env.NODE_ENV === 'development' ? '[DEV] 代理切换器' : '代理切换器', - version: '1.0.0', - description: '一个强大的浏览器代理管理扩展', - }, - // 其他配置... - }); - ``` - -5. **消息通信**: 使用结构化消息系统。 - - ```typescript - // utils/messaging.ts - export type MessageType = - | { type: 'SWITCH_PROXY'; proxyId: string } - | { type: 'GET_CURRENT_PROXY' } - | { type: 'PROXY_CHANGED'; proxyId: string }; - - export function sendMessage(message: T): Promise { - return browser.runtime.sendMessage(message); - } - ``` - -6. **图标状态管理**: 根据当前代理状态更新扩展图标。 - - ```typescript - // background/proxy.ts - function updateExtensionIcon(proxyId: string | null) { - const iconPath = proxyId - ? '/icons/proxy-active.png' - : '/icons/proxy-inactive.png'; - - browser.action.setIcon({ path: iconPath }); - } - ``` - -7. **错误处理**: 实现良好的错误捕获和报告。 - - ```typescript - // utils/error.ts - export async function executeWithErrorHandling( - fn: () => Promise, - errorMessage = '执行操作时出错' - ): Promise { - try { - return await fn(); - } catch (error) { - console.error(`${errorMessage}:`, error); - browser.notifications.create({ - type: 'basic', - iconUrl: '/icon-48.png', - title: '代理切换器错误', - message: errorMessage - }); - return null; - } - } - ``` - -8. **使用现代钩子**: 为React组件编写自定义钩子。 - - ```typescript - // hooks/useProxies.ts - import { useState, useEffect } from 'react'; - import { storage } from '../utils/storage'; - import type { Proxy } from '../types'; - - export function useProxies() { - const [proxies, setProxies] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const load = async () => { - const data = await storage.proxies.get(); - setProxies(data); - setLoading(false); - }; - - load(); - - return storage.proxies.subscribe(newProxies => { - setProxies(newProxies); - }); - }, []); - - return { proxies, loading }; - } - ``` - -## 版本兼容性 - -本指南适用于: -- WXT v0.20.0 及以上 -- React 18+ -- TypeScript 5.0+ - -## 扩展功能实现 - -### 代理管理功能 - -```typescript -// utils/proxy.ts -import { storage } from './storage'; -import { v4 as uuidv4 } from 'uuid'; -import type { Proxy } from '../types'; - -export async function getProxyList(): Promise { - return await storage.proxies.get(); -} - -export async function getCurrentProxy(): Promise { - return await storage.currentProxyId.get(); -} - -export async function switchProxy(proxyId: string | null): Promise { - // 更新存储 - await storage.currentProxyId.set(proxyId); - - if (!proxyId) { - // 清除代理 - await browser.proxy.settings.clear({}); - return; - } - - // 获取代理详情 - const proxies = await storage.proxies.get(); - const proxy = proxies.find(p => p.id === proxyId); - - if (!proxy) return; - - // 设置代理 - await browser.proxy.settings.set({ - value: { - mode: 'fixed_servers', - rules: { - proxyForHttp: { - scheme: proxy.protocol, - host: proxy.host, - port: parseInt(proxy.port) - }, - proxyForHttps: { - scheme: proxy.protocol, - host: proxy.host, - port: parseInt(proxy.port) - } - } - }, - scope: 'regular' - }); -} - -export async function saveProxy(proxy: Omit): Promise { - const newProxy: Proxy = { - ...proxy, - id: uuidv4() - }; - - const proxies = await storage.proxies.get(); - await storage.proxies.set([...proxies, newProxy]); - - return newProxy; -} - -export async function deleteProxy(proxyId: string): Promise { - const proxies = await storage.proxies.get(); - await storage.proxies.set(proxies.filter(p => p.id !== proxyId)); - - // 如果删除的是当前使用的代理,清除当前代理 - const currentProxyId = await storage.currentProxyId.get(); - if (currentProxyId === proxyId) { - await storage.currentProxyId.set(null); - await browser.proxy.settings.clear({}); - } -} -``` - -## 相关资源 - -- [WXT 官方文档](mdc:https:/wxt.dev) -- [WXT GitHub 仓库](mdc:https:/github.com/wxt-dev/wxt) -- [Chrome 扩展 API 文档](mdc:https:/developer.chrome.com/docs/extensions/reference) -- [React 文档](mdc:https:/reactjs.org) -- [TypeScript 文档](mdc:https:/www.typescriptlang.org) diff --git a/.gitignore b/.gitignore index 64f4000..4233a15 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ web-ext.config.ts *.sw? -ord/ \ No newline at end of file +ord/ +.artifacts/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..debb579 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,175 @@ +# Yakit Browser Agent Architecture + +## Goals + +- Reuse a user's real, authenticated browser session without exporting a complete browser profile. +- Let an AI agent inspect a deliberately shared tab and request human takeover for QR codes, MFA, CAPTCHA, or device confirmation. +- Keep proxy, Cookie, User-Agent, page-context, and page-function capabilities behind one typed command boundary. +- Make grants short lived, tab scoped, visible, and revocable. + +## Layers + +### Capability layer + +The background service owns browser capabilities. Every remote command passes through one router before it reaches browser APIs. + +| Method | Required scope | Effect | +| --- | --- | --- | +| `browser.tabs` | `browser.tabs.read` | Lists only tabs included in the active grant | +| `browser.frames` | `browser.tabs.read` | Lists main, same-origin, and cross-origin frames for a granted tab | +| `browser.context` | `browser.dom.read` | Captures a bounded structured snapshot and diff; Storage and Cookie require their own scopes | +| `browser.node.inspect` | `browser.dom.read` | Inspects a document-bound node without returning the current input value | +| `browser.node.action` | `browser.dom.write` | Clicks, focuses, scrolls, or writes a value through a current node reference | +| `browser.cookies` | `browser.cookies.read` | Reads cookies for a granted tab | +| `browser.takeover` | `browser.tab.activate` | Focuses a granted tab for a human step | +| `browser.handoff.request` | `browser.human.takeover` | Starts a visible QR/MFA/CAPTCHA/device-confirmation handoff | +| `browser.handoff.status` | `browser.human.takeover` | Reads the current task's handoff state | +| `browser.network.status/list` | `browser.network.read` | Reads capture state and request metadata | +| `browser.network.start/stop/clear` | `browser.network.capture` | Controls a bounded capture session for a granted document | +| `browser.network.export` | `browser.network.sensitive.read` | Builds a replay packet from explicitly captured headers/body | +| `browser.invoke` | `browser.page.invoke` | Calls an existing page-world function by path | +| `browser.eval` expression | `browser.page.eval.expression` | Executes one parenthesized expression in a granted page world | +| `browser.eval` program | `browser.page.eval.program` | Executes statements and side effects under an independent high-risk scope | +| `browser.observe.*` | `browser.observation.read/control/sensitive.read` | Controls bounded Fetch/XHR/Form/WebSocket/WebCrypto/CryptoJS observation | +| `proxy.list` | `browser.proxy.read` | Lists extension proxy profiles | +| `proxy.switch` | `browser.proxy.write` | Switches the browser proxy profile | + +The transport never calls browser APIs directly. + +### Grant layer + +A grant contains: + +- an unpredictable session ID; +- a task ID; +- one or more explicit tab, frame, and document IDs with their origin and grant-time URL; +- an explicit set of capability scopes; +- creation and expiration timestamps. + +Expired grants are rejected and removed. Reloading or navigating a document returns `stale_document`; navigation to a different origin returns `origin_changed`. Neither condition silently retargets an operation. The UI still offers read/control presets, but those presets only create concrete scope sets and are not stored as authorization levels. A remote caller cannot expand a grant. Only extension UI initiated by the user can create or replace one. + +### Transport layer + +Bridge v3 supports authenticated loopback WebSocket and optional Native Messaging: + +```text +Browser extension -> ws://127.0.0.1:/extension -> Yak engine / AI session +``` + +The Yak gRPC process owns this listener and starts it on `127.0.0.1:64333` by default. Yakit controls it through the existing `RequestYakURL` RPC with the `browser-extension://` schema, so pairing, approval, device rename and revocation do not add dedicated gRPC methods. + +First-time pairing uses `/pairing`. The extension generates an origin-bound ECDSA P-256 installation identity and keeps its non-extractable private key in IndexedDB. Yak keeps a persistent engine identity under the Yakit home directory with owner-only file permissions. The plugin and Yakit derive the same six-digit code from both nonces, identities, origin and public keys; the user approves only after comparing that code. No bearer token is stored or copied. + +The browser-profile `installationId` is stable across disconnects and local unpairing; clearing a pairing destroys the local signing key but does not manufacture a new browser installation. A later approved pairing with the same installation ID rotates the public credential in place while preserving the Yak `deviceId`, user-visible name and creation time. If browser storage was actually erased and a new installation ID is unavoidable, Yakit must explicitly choose whether to replace a matching offline identity or add a separate browser profile. Replacement is restricted to the same extension origin and client, so a shared Chrome extension ID is never used as an unsafe global deduplication key. + +Every `/extension` connection starts with a signed engine challenge. The extension verifies the approved engine public key and replies with a signature from its paired installation key. Yak verifies both the installation ID and browser extension Origin before returning `hello_ack`. The connection is not reported ready until that acknowledgement confirms protocol, capabilities, engine identity, engine instance, connection and session identities. The authentication message also carries the current task/grant identity. A disconnected installation can resume its logical session while each physical connection receives a new ID. Revoking a device immediately closes its active connection. Heartbeats carry sequence/timestamps and expose round-trip latency. + +Request IDs allow concurrent calls in both directions. The extension accepts at most eight engine-initiated in-flight requests, rejects duplicate IDs, supports cancellation, and applies a 16 MiB aggregate limit. Messages above 512 KiB are split into bounded 256 KiB chunks with transfer count/timeout limits. Yak forwards context cancellation and buffers extension events in a bounded queue exposed as `browser.ExtensionWaitEvent`. + +### Yakit device tasks + +Pairing and device CRUD remain on `RequestYakURL`. Executable work uses one server-streaming RPC, `ExecuteBrowserExtensionTask`, with stable routing fields (`task_id`, `device_id`, `schema`, JSON payload and timeout). The initial schemas are: + +- `capability.call`: invokes one extension capability with `{method, params}` and returns its JSON result; +- `yak.script`: executes Yak in the owning gRPC process and injects a request-bound `browser.ExtensionCall` and `browser.ExtensionStatus` for the selected device. + +The engine supports multiple simultaneous browser connections. Calls are routed by paired device ID, pending responses are bound to the target WebSocket, and a disconnect immediately fails that device's outstanding calls. A schema handler cannot silently fall back to another online browser. + +Task events use a small common vocabulary (`queued`, `running`, `log`, `result`, `warning`, `error`, `cancelled`, `completed`) with monotonic sequence and timestamp fields. The RPC bounds payload size, timeout, concurrent scripts, per-event data and aggregate output; cancelling the stream propagates through the Yak context to the extension request. + +The Yak runner is a controlled in-process context, not an operating-system sandbox. It prevents process exit, recovers VM panics and enforces resource bounds, but only trusted operator-authored code should use it. Untrusted or remotely supplied scripts require a future isolated worker. The generic `ExecYakScript` path is intentionally not reused because its child process does not own the parent process's live Bridge manager. + +Native Messaging uses the same Bridge v3 challenge/auth envelope and paired identity contract: + +```text +Browser extension -> registered Yakit Native Host -> loopback Yak Bridge -> running Yak engine +``` + +The Yak repository contains `common/browser/nativehostcmd`, a stdio framing proxy with loopback/origin validation. `native-host/install.sh` and `install.ps1` register per-user Chrome/Chromium/Edge/Brave/Firefox manifests. `nativeMessaging` is optional and requested only when the user explicitly saves Native mode. + +## Human takeover + +Agent workflows treat human participation as an explicit, persisted state transition: + +1. The agent detects a QR code, MFA prompt, CAPTCHA, or device confirmation. +2. It calls `browser.handoff.request` for a document in the active control grant. +3. The extension focuses the tab, shows a badge, expands the target page panel, and displays the same request in Popup and Options. +4. The Agent pauses without polling sensitive content. +5. The user chooses **操作已完成** or **取消任务**. +6. The extension emits `browser.handoff.changed`; Yak receives it through `ExtensionWaitEvent`. +7. The Agent matches the handoff ID, captures a fresh context, and continues only after `completed`. + +`browser.takeover` remains a short-lived focus action without a completion lifecycle. + +## Network capture + +Network capture uses the browser `webRequest` API rather than page-world Fetch/XHR monkey patches. This preserves the actual outgoing request headers, browser-added Cookie header, request body, redirect status, cache state, and timing. The listener is filtered to Fetch/XHR, ping, and related programmatic requests; images, stylesheets, scripts, fonts, and media are not collected. + +Each capture session is bound to one tab, frame, and document. Chrome MV3 stores the bounded session in `storage.session`, so Service Worker suspension does not move sensitive records into persistent settings. Firefox MV2 keeps the same data in background memory. Defaults are metadata-only, 100 entries, and no request headers or body. Explicit sensitive capture is capped at 200 entries and 64 KiB per request body; the UI currently uses 100 entries and 32 KiB. + +Generating a replay packet requires captured request headers. The packet is reconstructed as HTTP/1.1 with the observed header values and bounded body bytes. Truncated or omitted bodies produce an explicit limitation warning. Sending to Yakit is a confirmed Bridge request: Yak validates a maximum 2 MiB packet, saves a Web Fuzzer page configuration in the current project database, broadcasts the new tab to Yakit, and returns its `pageId` before the extension reports success. + +## Page-world code + +### Structured context and node references + +`browser.context` no longer returns a full HTML document. A snapshot contains a 20 KiB body-text excerpt, bounded headings/forms, up to 400 actionable nodes discovered while scanning at most 10,000 elements, a full frame inventory, optional bounded Web Storage values, optional IndexedDB database/store/key metadata, optional CacheStorage names, bounded document/SPA lifecycle events, optional Cookie values, authentication signals, and a diff against the preceding snapshot for the same tab/frame. IndexedDB and Cache values are never collected. Open Shadow Roots are traversed recursively; the extension's own edge-panel Shadow Root is excluded. + +Each actionable element is registered in the page's MAIN world and identified by `captureId + tabId + frameId + documentId + nodeId`. `browser.node.inspect` and `browser.node.action` resolve the registered `Element` directly instead of re-running a CSS selector. A new capture replaces the registry, a detached element is rejected, and a changed document fails target resolution. These paths return `stale_node` or `stale_document`; they never silently retarget a similar element. + +Frame inventory combines `webNavigation.getAllFrames` with a bounded packaged probe in every accessible frame. Grants store an explicit target for each selected `tabId + frameId + documentId + origin`; selecting a tab authorizes only its main frame until the user separately selects child frames. `webNavigation.getFrame` verifies each remote operation against the current frame URL and document. Cross-origin navigation returns `origin_changed`, while same-origin document replacement returns `stale_document`. + +Node inspection returns bounded identity, safe attributes, visibility, state, and viewport bounds. It deliberately excludes the current input value. Node actions support `click`, `focus`, `scroll`, and `setValue`; `setValue` uses native value setters plus input/change events, rejects file inputs, requires `browser.dom.write`, and never sends the supplied value to the audit writer. Programmatic click is a page-world click and is not represented as a trusted physical mouse event. + +The authentication classification is a heuristic based on bounded DOM controls plus explicitly requested Cookie names and Storage keys. It is useful for workflow routing, but it is not proof that the server accepts the current session. + +`PageExecutionAdapter` selects an execution mechanism at build time. Production/store Chrome builds use the Web Store-permitted User Scripts API: + +```text +Background capability router + -> userScripts.execute({ world: "MAIN" }) + -> structured { ok, result | error } +``` + +The default production build declares Chrome 138+, requires the user to enable Allow User Scripts, and physically omits `page-main-world.js`. It never silently falls back to direct Eval. + +User Scripts receive the selected expression or program as direct script source; the Store path never calls `eval` on Bridge-provided text. Expression mode automatically returns its expression. Program mode is an async function body and requires an explicit `return` to produce a value; without one it returns `undefined`. + +Development and local Firefox MV2 builds use WXT's packaged injection pattern. Enterprise Chrome prefers User Scripts and retains this pattern only as a managed fallback: + +```text +Background capability router + | tabs.sendMessage (extension-only) +Isolated content script + | correlated CustomEvent on the injected script element +Unlisted page-main-world script + | indirect eval / function invocation +The page's real window context +``` + +The old extension established the essential behavior by injecting `inject.js` and forwarding `CONTENT_EVAL_CODE` through `window.postMessage`. The current bridge preserves that capability while adding request IDs, Promise resolution, response timeouts, error propagation, cycle-safe result serialization, output limits, and content-script lifecycle cleanup. A timeout stops the extension from waiting for an asynchronous result; JavaScript cannot safely interrupt synchronous code, so an infinite loop can still block the target page. + +Both adapters share the same expression/program return rules, result serializer, Promise behavior, timeout bounds, and error envelope. Local Eval is initiated by an explicit user action. Remote expression and program modes require separate scopes and a target whose tab, frame, document and origin still match. Because the page controls its JavaScript environment, all results remain untrusted input. + +The public Firefox MV3 AMO channel is invoke-only at the extension boundary: it requests neither `userScripts` nor general page invocation/Eval, does not package `page-main-world.js`, and advertises neither Bridge capability. This follows Mozilla's current restriction of `userScripts` to user-script managers. Structured context, stable node commands, network capture, observation and human handoff remain available. + +The same page bridge supports `browser.invoke` for the narrower case where the Agent already knows a concrete global function path. Fetch/XHR/Form/WebSocket/WebCrypto/CryptoJS observation uses the same grant and lifecycle boundaries through a separate bounded MAIN-world observer. + +## Page UI loading + +The content script is a roughly 10-12.2 KiB native DOM shell. It owns the Yak launcher, bridge indicator, drag position, left/right snapping, and handoff-triggered expansion. React, Radix, and the floating workbench are loaded in `floating.html` only after the user expands the launcher or a handoff targets that tab; the iframe is released after 60 seconds collapsed. Build auditing prevents the content script from exceeding its size budget. + +Popup, Options, and the floating workbench share one token-based design system in `src/styles/`: `tokens.css` defines the palette, type scale (11-20px), radii, and shadows, including a full dark set under `[data-theme='dark']`; `ui.css` styles the shared Radix-backed components. The vivid brand orange is reserved for non-text accents; filled primary buttons and text links use a deeper AA-contrast orange. All surfaces are light-first — the orange yak mark is shown bare without a backing tile. The theme preference (`system`/`light`/`dark`) lives in its own `settings.appearance.v1` local-storage key, is written only from extension UI, and is applied to `` by each entrypoint through `src/platform/storage/appearance.ts`; the content-script launcher reads the same key in-page (falling back to the OS scheme) to theme its shadow-DOM shell. + +## Audit boundary + +Audit events live under a separate storage key and are serialized independently from settings and active session state. The bounded log retains the latest 500 events. It records category, method/action, outcome, task ID, tab ID, duration, error code, and a fixed safe summary where applicable. Capability parameters and results are never passed to the audit writer. The Options activity view reads the latest 200 entries and lets the user clear them locally. + +## Production operations + +- State v7 uses separate durable proxy/UA/Bridge/panel keys and separate session grant/Bridge/action keys; mutation is serialized across domains and no legacy migration path exists. +- Agent actions have a session timeline and user pause/resume/revoke controls. Persistent audit remains metadata-only. +- Managed storage can lock transport, endpoint/host, grant duration/origins, program Eval and panel availability. Enforcement is in background handlers. +- Aggregate Service Worker, Bridge, heartbeat and capability metrics stay local. Explicit diagnostics export omits URLs, values, payloads, Eval code and task/grant identifiers. +- Public review artifacts live under `docs/store-review`; privacy, permission and enterprise deployment contracts live under `docs/`. +- Store/Enterprise Chromium E2E covers 320/390/desktop UI, service-worker restart, frame/document/origin boundaries, request/observation workflows, handoff, audit/diagnostic redaction and state concurrency. Go tests cover Bridge v3 pairing, code derivation, signed challenge/auth, revocation, YakURL control, chunking/session recovery and Native Messaging proxy framing. diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..a28d441 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,69 @@ +# Design System: Yakit Browser Agent +**Project ID:** yakit-chrome-client (derived from codebase design tokens, `src/styles/tokens.css` — no Stitch project) + +## 1. Visual Theme & Atmosphere + +A **focused security instrument panel**: utilitarian, information-dense, and calm. The aesthetic philosophy is "console first, chrome second" — content surfaces stay quiet and neutral so that state (connection, capture, risk) can carry all the visual signal. The mood is airy-but-dense: compact 13px typography and tight 8px-rhythm spacing, balanced by generous card padding and breathing room between functional groups. + +The brand presence is deliberately restrained: a light, continuous surface carries every view, signed by the bare orange yak mark and a single ember-orange accent reserved for moments of genuine emphasis. Nothing glows, nothing gradients, no black slabs; depth comes from whisper-soft shadows and hairline separators, not borders. The system ships in twin themes — a cool light canvas and a true-dark console — with identical geometry and hierarchy, switched by a user preference (`system` / `light` / `dark`). + +## 2. Color Palette & Roles + +### Light theme (default) + +- **Canvas Mist (#f3f4f6)** — application background; lets white cards float without borders. +- **Card White (#ffffff)** — primary content surfaces: cards, tables, panels, inputs. +- **Inset Pebble (#eceef1)** — recessed fills: stat tiles, code-free inset areas, toggle-off track. +- **Ink (#1d232a)** — primary text and strong values. +- **Slate Note (#68727d)** — secondary text, descriptions, timestamps. +- **Label Slate (#474f59)** — field labels, section labels, ghost-button text. +- **Hairline (#e1e4e8)** — non-structural separators (table rows, list dividers); used sparingly. +- **Frame Line (#c8cfd6)** — input strokes and secondary-button outlines. +- **Yak Orange (#ee7815)** — brand accent for *non-text* signal only: toggle-on tracks, active nav indicator, icon highlights, focus halo. Never carries text. +- **Ember (#b54f08)** — the accessible action orange: filled primary buttons (white text, 5.1:1 AA) and text links on light surfaces. +- **Ember Deep (#9e4607)** — hover state for filled primary buttons. +- **Ember Wash (#fdf0e1)** — soft selection tint: active list rows, selected table lines. +- **Pine (#1e7f52)** on **Mint Mist (#e4f3eb)** — connected, captured, success states. +- **Umber (#94650d)** on **Parchment (#fcf2d9)** — warning states and the human-handoff surface. +- **Brick (#bf3d3d)** on **Blush (#fbeaea)** — destructive actions, errors, failed states. +- **Bare Yak (the orange brand mark, #f97a04 family)** — shown directly on the surface with no backing tile; it is the only persistent brand signature. + +### Dark theme (`[data-theme='dark']`) + +- **Deep Space (#0e1116)** — application background; true console dark, not navy. +- **Panel Slate (#161b21)** — cards and surfaces. +- **Raised Slate (#1e242c)** — inset fills and hover states. +- **Fog Text (#e2e7ec)** — primary text; **Ash (#8a949f)** secondary; **Mist Strong (#b2bcc5)** labels. +- **Ember Glow (#f5832a)** — filled primary buttons with **Roasted Ink (#201205)** text (7.5:1 AA); brighter than light theme to hold contrast on dark. +- **Ember Light (#f7a15c)** — text links and code accents on dark surfaces. +- Semantic tints deepen to translucent darkness: **Pine Glow (#45b981 / #122a1f)**, **Amber Glow (#d9a441 / #2c2311)**, **Coral Glow (#e06e6e / #2f1b1b)**. + +## 3. Typography Rules + +- **Family:** A system-native sans stack (Inter falling back to ui-sans-serif, system-ui, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Noto Sans CJK SC) — chosen for crisp CJK rendering at small sizes without bundling font files. Code and packets use a mono stack (ui-monospace, SF Mono, Consolas). +- **Scale (six steps, no more):** 11px for uppercase micro-labels only, 12px secondary/description, **13px as the reading base**, 14px emphasized values, 16px section titles, 20px page titles. +- **Weight hierarchy:** 500 for navigation, 600 for interactive text and labels, 650 for card titles and strong values, 700 reserved for page titles and hero numerals. +- **Micro-labels:** 11px, weight 650, letter-spacing .04em, uppercase, Slate Note color — the "eyebrow" voice used above data. +- **Rhythm:** line-heights stay tight (16–18px for body); Chinese text is never set below 12px except uppercase micro-labels. + +## 4. Component Stylings + +* **Buttons:** 36px tall with gently squared corners (6px radius) and 13px semibold labels. The *filled primary* is Ember (light) / Ember Glow (dark) with contrasting text — strictly one per view. *Secondary* buttons are Card White with a Frame Line stroke. *Ghost* buttons are transparent until hovered. *Danger* is a Brick outline that fills with Blush on hover. Small (30px) and icon (34px square) variants share the same geometry. +* **Cards/Containers:** Generously rounded corners (12px radius), Card White fill, and a whisper-soft two-layer shadow (a 1px key line of shade plus a faint 4px lift) — no borders. Recessed stat tiles inside cards use Inset Pebble with softly rounded corners (8px). Nothing nests a shadowed card inside another. +* **Inputs/Forms:** 36px tall, 6px corner radius, 1px Frame Line stroke on Card White; textareas keep the same stroke. Focus never shows a hard outline — instead a soft ember halo (3px of translucent Yak Orange). Field labels are 12px semibold Label Slate; hints in 12px Slate Note. +* **Toggles:** Pill-shaped switches (40×22px), Pebble track when off, Yak Orange track when on, white 16px thumb gliding on a short ease. +* **Navigation rail:** A 238px rail in the same surface as the workspace, separated by a single hairline. Items are 40px rows with softly rounded corners (8px); the active item shows a subtle raised fill plus a 3px inset Yak Orange indicator bar on its leading edge, with its icon tinted orange. +* **Status pills:** Fully rounded (pill-shaped, 999px) badges pairing each semantic color with its soft wash — connected/capturing in Pine-on-Mint, waiting/warning in Umber-on-Parchment, error in Brick-on-Blush. +* **Code & packets:** Deep slate panels (#171b20, light mono text) with 8px rounded corners; they remain dark in both themes as "terminal territory." +* **Handoff surface:** A Parchment card with a 3px Umber leading edge and the warning icon — the single interruptive pattern in the system, reserved for QR/MFA/CAPTCHA human takeover. + +## 5. Layout Principles + +- **Shell:** A fixed 238px dark rail plus a fluid workspace. The workspace column is capped at a comfortable 1440px reading width and **horizontally centered**, so ultra-wide monitors frame the console instead of stretching tables into unreadability. +- **Grid alignment:** The 60px sticky topbar shares the exact content grid — its padding is computed from the same 1440px cap (`max(28px, (100% − 1440px)/2 + 28px)`), keeping the target-tab chip and the page content on one vertical line. +- **Spacing rhythm:** An 8px base unit; 16px gaps between cards, 16–20px inner card padding, 22–28px page padding. Groups are separated by space and shadow, not rules. +- **Two-column workbenches:** Data pages (network, cookies, context, engine) use a fluid primary column with a 320–440px inspector column that sticks below the topbar; below 1080px they stack to a single column. +- **Grid discipline:** Every single-column vertical grid declares an explicit `minmax(0, 1fr)` track, so long URLs and code strings truncate with ellipses instead of overflowing narrow (320–390px) viewports. +- **Popup:** A fixed 390px single-sheet column — sections divided by hairlines, not floating cards — designed to a strict 600px height budget, keeping every action including the bottom primary capture button visible without scrolling. +- **Floating panel:** A 46px edge launcher — a white stadium orb with the bare yak mark (dark in dark theme, theme-aware in-page) — that expands to a 326px rounded workbench over the page; its header shares the panel surface with a single hairline seam. +- **Motion:** Short (140–180ms) ease transitions on color and slide only; `prefers-reduced-motion` collapses all animation. diff --git a/README.md b/README.md index 78cf194..c0be318 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,84 @@ -# WXT + React +# Yakit Browser Agent -This template should help get you started developing with React in WXT. +Browser security tools and a consent-gated context bridge for Yak AI agents. + +The WXT extension includes proxy profiles and PAC routing rules, Cookie and User-Agent tools, a Shadow DOM edge panel, authenticated-tab context capture, and controlled execution in the page's real JavaScript world. Structured context uses bounded text, forms, authentication signals, open Shadow DOM traversal, context diffs, and document-bound node references instead of exporting full page HTML. AI access is bound to a concrete tab, frame, document, origin, task, scope set, and expiration time. Yak/Yakit product assets are kept in `public/` and exposed to content scripts through explicit web-accessible resources. + +When an Agent reaches a QR code, MFA, CAPTCHA, or device confirmation, it can create a human handoff. The target tab is focused, the extension presents the request in Popup, Options, and the edge panel, and the Agent receives a completion or cancellation event after the user decides. The network workspace can capture a granted document's real Fetch/XHR requests and open an authenticated replay packet in Yakit Web Fuzzer. Sensitive headers, Cookie, and body capture are off by default and session-only. A separate local audit log stores only method, target, timing, and outcome metadata; it does not store page content, Cookie values, Eval source, network payloads, arguments, or results. + +## Development + +```bash +pnpm install +pnpm dev +``` + +WXT intentionally refuses to launch browsers automatically when it detects WSL, even when WSLg and a Linux Chrome are available. Use the project runner instead: + +```bash +pnpm dev:wsl +``` + +It keeps the development profile in `.wxt/chrome-wsl-profile`. Official Chrome 137+ no longer accepts `--load-extension`, so the first run opens `chrome://extensions`: enable Developer mode and load `.output/chrome-mv3-dev` once. The profile remembers it on later runs. + +Chromium and Chrome for Testing still support automatic loading. Select one with: + +```bash +CHROME_PATH=/path/to/chromium pnpm dev:wsl +``` + +Production builds: + +```bash +# Chrome Web Store: User Scripts MAIN, no direct Eval bridge +pnpm build +# Explicitly named store output +pnpm build:store +# Managed/local deployment: User Scripts MAIN with packaged bridge fallback +pnpm build:enterprise +# Local/enterprise Firefox MV2 injected bridge +pnpm build:firefox +# Public Firefox MV3 AMO invoke-only package +pnpm build:firefox:amo +``` + +Chrome 138+ requires the user to enable **Allow User Scripts** on the extension details page before the store build can run page-world Eval. The extension reports this condition explicitly and does not fall back to direct Eval. + +Production verification: + +```bash +pnpm verify:production +pnpm verify:ui:store +pnpm verify:ui:enterprise +pnpm verify:ui:enterprise:fallback +pnpm verify:native +``` + +`verify:production` runs Vitest and enforces content-script, background, total-size, permission, managed-policy, execution-channel, `webRequest`, and web-accessible-resource budgets across four packages. Browser E2E covers Chrome Store User Scripts, Enterprise User Scripts, and the Enterprise injected fallback, including document-bound grants, context diff, stable node operations, expression/program scope separation, pause/resume/revoke, human handoff, request/crypto observation, Yakit workflows, split storage, Service Worker restart, audit/diagnostic redaction, strict CSP, fail-closed tab teardown, and 320/390/desktop UI bounds. `verify:native` builds the Go host and exercises Chromium Native Messaging through the host into a loopback Yak Bridge fixture; because Playwright cannot operate Chrome's toolbar permission prompt, only its disposable test copy pre-grants `nativeMessaging`, while the source Store package is asserted to remain optional. + +Browser verification prefers `CHROMIUM_PATH`, then `CHROME_PATH`, Playwright's Chromium cache, Chrome for Testing, or system Chromium. It deliberately does not auto-select stable Google Chrome because current stable Chrome ignores unattended `--load-extension` startup flags. + +## Pair with Yak and Yakit + +The Yak gRPC process owns the local browser Bridge. The standard command starts Bridge v3 on `127.0.0.1:64333` automatically, so there is no separate Bridge script or shared token to configure: + +```bash +go run common/yak/cmd/yak.go grpc --host 0.0.0.0 +``` + +Open **系统设置 -> 浏览器集成** in Yakit, then open **引擎连接** in the extension and choose **查找本机 Yakit**. Both surfaces display the same six-digit verification code. Compare the code and approve the pending browser in Yakit. The approval persists an origin-bound device identity; later connections authenticate automatically with signed challenges. Removing the device in Yakit immediately disconnects it and requires a new approval. + +To run a browser task, create a sharing grant for the target tab in the extension, return to **系统设置 -> 浏览器集成**, and click the online browser row. The device workspace can call a scoped capability directly or run Yak code with a request-bound `browser.ExtensionCall`. Task state, logs, JSON results, cancellation, and errors are streamed in that workspace. Do not use the generic `ExecYakScript`/`grpc_execYak` runner for this flow: that runner starts a child Yak process and cannot own the parent gRPC process's live browser connections. + +Advanced transport settings remain available for a non-default loopback port or Native Messaging deployment. `--browser-extension-bridge-port` changes the Yak listener, and `--disable-browser-extension-bridge` disables it explicitly. + +## Native Host and deployment + +Build the Native Messaging transport from the Yak repository and register it with the signed or unpacked extension ID: + +```bash +go build -o yakit-browser-agent-host ./common/browser/nativehostcmd +./native-host/install.sh --host-binary /absolute/path/to/yakit-browser-agent-host --extension-id YOUR_EXTENSION_ID +``` + +Windows uses `native-host/install.ps1`. Native Messaging is an optional browser permission requested only when Native mode is selected. See [Native Host installation](native-host/README.md), [enterprise policy](docs/ENTERPRISE_POLICY.md), [permissions](docs/PERMISSIONS.md), [privacy](docs/PRIVACY_POLICY.md), and the [release review packet](docs/store-review/RELEASE_CHECKLIST.md). diff --git a/docs/ENTERPRISE_POLICY.md b/docs/ENTERPRISE_POLICY.md new file mode 100644 index 0000000..b865165 --- /dev/null +++ b/docs/ENTERPRISE_POLICY.md @@ -0,0 +1,38 @@ +# Enterprise Deployment + +The extension includes `managed-storage-schema.json`. Managed values are read-only and rechecked by background command handlers, not only reflected in disabled UI controls. + +Supported policies: + +| Key | Type | Effect | +| --- | --- | --- | +| `bridgeTransport` | `native` or `websocket` | Locks transport. | +| `bridgeEndpoint` | string | Locks the explicit loopback WebSocket endpoint. | +| `nativeHost` | string | Locks the Native Messaging host name. | +| `autoConnect` | boolean | Locks startup connection behavior. | +| `disableWebSocket` | boolean | Requires Native Messaging. | +| `floatingPanelEnabled` | boolean | Enables or disables the page panel. | +| `maxGrantMinutes` | integer, 5-1440 | Caps every grant even if the UI requests longer. | +| `grantAllowedOrigins` | origin array | Rejects grants containing any other origin. | +| `allowProgramEval` | boolean | Can prohibit the independent program Eval scope. | + +Example managed policy values: + +```json +{ + "bridgeTransport": "native", + "nativeHost": "com.yaklang.browser_agent", + "autoConnect": true, + "disableWebSocket": true, + "maxGrantMinutes": 60, + "grantAllowedOrigins": ["https://security-lab.example"], + "allowProgramEval": false, + "floatingPanelEnabled": true +} +``` + +Chrome/Edge administrators distribute these values using the platform's extension managed-storage policy and can force-install the signed extension ID. Firefox administrators use the `3rdparty.Extensions` policy for the add-on ID `browser-agent@yaklang.com`. Native Host registration remains an operating-system deployment step; see [native-host/README.md](../native-host/README.md). + +The Chrome enterprise package prefers User Scripts MAIN for CSP-compatible page execution and retains the packaged injected bridge as a fallback when User Scripts is unavailable. Administrators should enable User Scripts for the extension in managed Chrome deployments when strict-site CSP execution is required; the fallback remains suitable for explicitly managed sites whose CSP permits it. + +Device pairing identities are deliberately excluded from managed storage. Pair each extension installation locally through Yakit. The extension keeps its non-extractable private key in IndexedDB and Yak stores only the approved public device identity, so a broadly readable policy backend never becomes a credential store. diff --git a/docs/PERMISSIONS.md b/docs/PERMISSIONS.md new file mode 100644 index 0000000..f12c88b --- /dev/null +++ b/docs/PERMISSIONS.md @@ -0,0 +1,22 @@ +# Permission Inventory + +Every permission maps to a shipped, user-facing feature. Future functionality is not a reason to retain an unused permission. + +| Permission | Purpose | User control | +| --- | --- | --- | +| `proxy` | Apply direct/system/fixed/PAC profiles and deterministic routing rules. | Profiles and rules are visible and switchable; passwords are session-only. | +| `storage` | Store split settings, active session, bounded audit and aggregate metrics. | Audit, action timeline and metrics can be cleared; diagnostics export is explicit. | +| `tabs` | Resolve the exact user-selected tab and open Options/Yakit workflow pages. | Grant and target picker identify the tab. | +| `scripting` | Run packaged frame probes, stable-node operations and page observation. | Page operations are explicit and scoped. | +| `cookies` | Provide the Cookie Editor and explicitly granted authentication context. | Values are hidden and exports redacted by default. | +| `declarativeNetRequest` | Change the real outbound User-Agent request header. | Named UA rules are visible and removable. | +| `webRequest` | Capture bounded Fetch/XHR/Form metadata and proxy rule hits. | Capture starts explicitly; headers/body are off by default. | +| `webNavigation` | Track frame/document identity and SPA/document lifecycle. | Used to reject stale or cross-origin targets. | +| `webRequestAuthProvider` (Chrome) / `webRequestBlocking` (Firefox) | Answer proxy authentication challenges. | Username is in the profile; password is browser-session-only. | +| `userScripts` (Chrome Store/Enterprise) | Execute user/Agent-selected page code through Chrome's documented MAIN-world User Scripts API. | Chrome also requires the user to enable Allow User Scripts; expression/program grants are separate. | +| `nativeMessaging` (optional) | Connect to the installed local Yakit Native Host. | Requested only when the user selects Native Host in Options. | +| `` host access | Support authenticated testing on the HTTP(S) site selected by the user, the floating task control, frame inventory and request capture. | Site panel rules and task-bound grants narrow actual Agent access. Browser-internal pages remain unavailable. | + +`activeTab` is intentionally not requested. Firefox AMO does not request `userScripts`; its public build is invoke-only and excludes general page function invocation/Eval. Chrome Store does not package the injected Eval bridge. + +References: [Chrome minimum permission policy FAQ](https://developer.chrome.com/docs/webstore/program-policies/user-data-faq), [Chrome MV3 requirements](https://developer.chrome.com/docs/webstore/program-policies/mv3-requirements), and [Mozilla Add-on Policies](https://extensionworkshop.com/documentation/publish/add-on-policies/). diff --git a/docs/PRIVACY_POLICY.md b/docs/PRIVACY_POLICY.md new file mode 100644 index 0000000..c72c566 --- /dev/null +++ b/docs/PRIVACY_POLICY.md @@ -0,0 +1,54 @@ +# Yakit Browser Agent Privacy Policy + +Effective date: 2026-07-17 + +Yakit Browser Agent is a browser security-testing extension that connects browser context selected by the user to a Yak/Yakit engine running on the same computer. This policy describes the extension source in this repository and its official packaged builds. + +## Data the extension handles + +Depending on the command the user selects and the grant scopes they enable, the extension can handle: + +- page URL, title, frame and document identity; +- bounded page text, forms, interactive element metadata, open Shadow DOM metadata, and authentication signals; +- Cookie metadata and values, including HttpOnly cookies exposed by the browser Cookies API; +- localStorage/sessionStorage keys, IndexedDB database/store/key inventory, and CacheStorage names; database and cache values are not collected; +- request URL, method, timing and status, plus request headers, Cookie and body only when sensitive capture is explicitly enabled; +- temporary Fetch/XHR/Form/WebSocket/WebCrypto/CryptoJS observations; value previews require a separate sensitive scope; +- proxy, User-Agent header, floating-panel and Bridge settings; +- local operational metrics such as aggregate Bridge latency, connection errors, capability duration and Service Worker starts. + +## How data is used + +Data is used only to provide user-facing browser security workflows: inspect an authenticated page, operate explicitly selected elements, replay a selected request in Yakit, analyze authentication/signing behavior, or let an Agent continue after user-controlled QR/MFA/CAPTCHA handling. It is not used for advertising, credit decisions, user profiling, sale, or unrelated analytics. + +## Data transmission + +The extension has no developer-operated telemetry or analytics endpoint. Browser context is sent only after a user creates a time-limited grant or invokes a clearly labeled workflow. The destination is the user-configured Native Messaging host or an explicit loopback WebSocket endpoint. The default is `ws://127.0.0.1:64333/extension`. + +The Native Host is a local transport to that loopback Yak Bridge. Bridge v3 still verifies the paired extension identity, browser extension Origin, task, grant, target and capability scopes. A website cannot access this channel. + +## Local storage and retention + +- Proxy, User-Agent, Bridge and floating-panel settings remain until the user changes them or removes the extension. +- The paired engine public identity and device ID are local settings. The extension's non-extractable P-256 private key remains in extension-owned IndexedDB; no reusable bearer token is stored. Proxy passwords, active grants, handoffs, action timelines, captured requests and observation values are session-scoped. +- Audit storage retains at most 500 metadata-only records. It omits page content, URLs, request parameters, Cookie/token values, Eval code, arguments and results. +- Context and request buffers are bounded and replaced or cleared by document, grant and session lifecycle. +- Operational metrics are aggregate local counters. They are included only when the user explicitly exports a diagnostics file. + +## User control + +The user selects the tab/frame, scopes and expiration for every Agent grant and can pause, resume or revoke it. Sensitive network fields, observation values and program Eval each require separate controls or scopes. Cookie values are hidden by default. Exports are redacted by default. The floating panel can be disabled globally, restricted to active tasks, or controlled with an allowlist/denylist. + +Removing the extension deletes browser-managed extension storage. The Native Host installer has an uninstall option that removes its per-user manifests and copied executable. + +## Security + +The WebSocket Bridge accepts explicit loopback hosts only. First-time pairing requires the user to compare a six-digit code in the extension and Yakit. Later handshakes use mutually verified P-256 signatures and identify the engine, extension installation, connection and resumable session. Revoking a paired device closes its active connection. Grants bind task, tab, frame, document, origin, scopes and expiry. Messages have runtime schemas, concurrency limits, cancellation, bounded payloads and chunk reassembly limits. + +No system can guarantee absolute security. Do not use the extension against systems you are not authorized to test, and do not include secrets in public bug reports. + +## Changes and contact + +Material policy changes must accompany a product update and updated store disclosures. Questions or security reports can be opened at [yaklang/yaklang issues](https://github.com/yaklang/yaklang/issues); use a private security-reporting channel for sensitive vulnerability details. + +Official policy references: [Chrome Web Store User Data FAQ](https://developer.chrome.com/docs/webstore/program-policies/user-data-faq), [Chrome Limited Use guidance](https://developer.chrome.com/docs/webstore/user_data), and [Mozilla Add-on Policies](https://extensionworkshop.com/documentation/publish/add-on-policies/). diff --git a/docs/PRODUCT_ARCHITECTURE_ROADMAP.md b/docs/PRODUCT_ARCHITECTURE_ROADMAP.md new file mode 100644 index 0000000..99f7298 --- /dev/null +++ b/docs/PRODUCT_ARCHITECTURE_ROADMAP.md @@ -0,0 +1,728 @@ +# Yakit Browser Agent 产品与架构路线 + +> 状态:Phase 1-4 源码、构建、测试与审核产物已完成;仅剩外部账号、签名与商店人工审核 +> 更新时间:2026-07-17 +> 适用仓库:`yaklang-chrome-extension`、Yak `common/browser`/`common/yak/yakurl` 与 Yakit 浏览器集成页 + +## 0. 2026-07-17 实施快照 + +本项目尚未正式发布,因此当前重构不承担旧状态、旧消息或旧 Bridge 协议的迁移兼容。破坏性变更直接形成新的生产基线,避免长期保留双字段、双协议和回退分支。 + +本轮已经落地: + +- 状态模型直接切换到 v7;代理、UA、Bridge、面板设置与 grant/Bridge/action session 分域存储,不读取旧聚合 key; +- content script、嵌入式 floating page、Popup/Options 三类发送者使用不同的标签页绑定策略; +- Options 顶栏显式选择目标标签页,从 Popup/悬浮面板进入时携带 `tabId`; +- RequestMap + Valibot 严格校验 extension runtime 消息和 Bridge method params; +- 授权改为 `targets + origin + scopes + taskId + expiresAt`,跨来源导航后失效; +- background 状态写入串行化,避免并发 `get -> modify -> set` 丢更新; +- Bridge v3 使用 `engine challenge -> extension auth -> hello_ack`,以双方 P-256 身份签名绑定 extension Origin、installation、engine、connection、session、task 与 grant; +- Yak gRPC 默认托管 loopback Bridge;Yakit 复用 `RequestYakURL` 的 `browser-extension://` schema 完成配对窗口、审批、重命名和撤销,没有增加成组 gRPC RPC; +- Bridge 运行时支持多浏览器同时在线并按 `deviceId` 隔离路由;Yakit 点击设备行可进入能力调用/Yak 脚本工作台,单一 `ExecuteBrowserExtensionTask` 流式 RPC 负责 schema 分发、日志、结果、取消和错误回程; +- 浏览器 Yak 任务在拥有 Bridge 的 gRPC 进程内执行,请求级注入选中设备的 `browser.ExtensionCall`,并限制脚本体积、并发、超时、单事件和总输出;不再借用会 fork 子进程的通用 Exec Yak 链路; +- 插件与 Yakit 展示同一六位校验码,审批后自动连接;不再配置、复制或轮换 bearer token,设备撤销会立即断开当前会话; +- 页面执行抽象为 Chrome User Scripts MAIN、受管 injected MAIN fallback 与 Firefox AMO invoke-only 渠道; +- 默认 production/store 构建使用 User Scripts,物理移除 `page-main-world.js`;enterprise 使用 User Scripts 优先并保留 injected fallback,dev 与 Firefox MV2 保留 injected bridge; +- 常驻 content script 从约 480KB 降到 Store 约 10.4 KiB;React 浮动工作台仅在展开时加载; +- Chrome Store/User Scripts 与 injected bridge 均通过真实 Chromium E2E; +- 构建预算、商店执行策略和资源暴露策略已经加入自动审计。 +- grant target 已绑定 `tabId + frameId + documentId + origin`,同源刷新返回 `stale_document`,跨来源导航返回 `origin_changed`; +- Bridge 已支持 cancel、8 请求并发上限、重复 ID 拒绝、16 MiB 收发上限和断线清理; +- 人工接管具备 `waiting_for_user -> completed/cancelled` 状态、三处 UI 提示和扩展到 Yak 的事件回程; +- 审计流使用独立 storage key,最多保留 500 条脱敏元数据,Options 提供操作记录视图; +- Store 与 Enterprise E2E 已覆盖接管完成事件、取消、审计脱敏和 document 边界。 +- 基于 `webRequest` 的 document-bound Fetch/XHR 捕获已经落地,默认只保存有界元数据;请求头、Cookie 和 body 需要显式开启; +- Options 已提供网络时间线、原始请求检查器和复制功能; +- 插件与 Yak 已支持双向 request/response,`yakit.web_fuzzer.open` 会保存配置、打开 Yakit Web Fuzzer 并返回 `pageId`; +- Store E2E 已验证真实 HttpOnly Cookie、请求头、POST body、Agent scope 读取、Web Fuzzer 回执和审计不泄漏。 +- 页面上下文已从最多 500 KiB HTML 改为有界结构化快照,正文摘要上限 20 KiB,可操作节点上限 400; +- `captureId + documentId + frameId + nodeId` 稳定引用、`browser.node.inspect/action` 和 `stale_node` 已落地; +- open Shadow DOM 遍历、认证信号、context diff 与登录态工作区已经完成,并通过真实 Chromium 节点写入/点击测试。 +- main/同源/跨源 frame inventory、显式 frame 授权与跨 frame context 已完成; +- IndexedDB database/store/key 概况、CacheStorage 名称清单和 SPA history/fragment 生命周期已完成,数据库与 Cache 值不会被采集。 +- Fetch/XHR/Form/WebSocket/WebCrypto/CryptoJS 独立 MAIN-world 观测器、敏感值独立 scope、Yak PoC 与无值 AI 分析上下文已完成; +- Cookie 三格式导入导出、UA 请求头边界、PAC 分流/认证/冲突/统计已完成; +- Bridge v3 已支持 512 KiB 阈值分片、16 MiB 总上限、心跳延迟、设备签名认证和逻辑 session 恢复; +- expression/program Eval 独立 scope、Agent session action timeline 与暂停/恢复/撤销已完成; +- 任务型 Overview、320/390px 导航、站点策略/活动任务/全屏/快捷展开悬浮面板已完成; +- Native Host 可执行程序、Linux/macOS/Windows 安装器、企业 managed policy、本地指标、脱敏诊断、权限/隐私/Limited Use/商店审核包已完成; +- Vitest 23 项、四渠道构建审计、Store/Enterprise Chromium E2E、Service Worker 重启验证、Native Messaging v3 真实链路与 Yak Go 确定性包测试已完成。 + +外部发布动作不属于源码可自动完成的状态:开发者账号、签名证书、稳定隐私政策 URL、Windows/macOS/Linux 真机签名包、Chrome Web Store/AMO 上传、审查往返与批准。执行清单位于 `docs/store-review/RELEASE_CHECKLIST.md`。 + +## 1. 当前判断 + +当前版本已经从一年前的实验性浏览器插件演进为可提交审核的生产候选基线: + +- WXT、React、Chrome MV3 与 Firefox 构建链路已经建立; +- Popup、Options 和网页悬浮面板使用统一的品牌与 UI 体系; +- Yak/Yakit 原始品牌资产已经恢复; +- 代理、Cookie、User-Agent、页面上下文和 Bridge 已经形成基础能力; +- Chrome Store User Scripts、Enterprise User Scripts + injected fallback 与 Firefox AMO invoke-only 发布边界已经物理分包; +- Bridge v3、Yakit 配对控制面、Native Host、task/grant/session 身份和授权有效期已经打通; +- 只读、表达式 Eval、程序 Eval、敏感网络与观测值分别授权; +- 扫码、MFA、CAPTCHA 接管和 Agent 暂停/恢复/撤销已经形成可观察状态机; +- 生产剩余风险已经收敛为外部签名、真机兼容与商店审核,而不是未实现的核心架构。 + +## 2. 产品北极星 + +Yakit Browser Agent 不应该被设计成另一个通用浏览器工具箱。 + +Cookie Editor、UA 修改、编码解码和代理切换都是辅助功能。产品真正有差异化的价值是: + +> 将用户真实登录后的浏览器环境,以明确授权、可观察、可暂停、可人工接管、可审计的方式交给 Yakit 和 AI Agent。 + +所有架构和 UI 决策都应服务于以下主流程: + +```text +用户选择目标标签页 + -> 创建与 AI task 绑定的授权 + -> Agent 读取结构化页面和认证上下文 + -> 捕获请求、签名或加密逻辑 + -> 发送到 Yakit Fuzzer / Repeater / AI + -> 遇到二维码、MFA、CAPTCHA 时请求人工接管 + -> 用户完成并显式恢复任务 + -> Agent 获取新上下文并继续 + -> 授权到期或用户主动撤销 +``` + +## 3. P0:继续扩展功能前必须处理 + +### 3.1 请求必须绑定发送者标签页 + +原实现的 background handler 忽略 `runtime.MessageSender`,content script 发出的 `tab.active`、`context.capture` 等请求会重新查询当前活动标签页。本轮已经完成 sender、frame 与 document 级绑定。 + +这会造成一个真实风险:后台标签页加载 content script 时,如果用户已经切到另一个标签页,悬浮面板可能显示、授权或采集错误的页面。 + +目标规则: + +```text +content script 请求 + -> 默认使用 sender.tab.id + sender.frameId + sender.documentId + +popup / options 请求 + -> 必须显式传 tabId,或由 UI 明确选择 active tab + +Bridge 请求 + -> 必须显式传 tabId,并验证它属于当前 grant +``` + +所有页面能力都应接受统一目标: + +```ts +interface BrowserTarget { + tabId: number; + frameId?: number; + documentId?: string; +} +``` + +导航后旧 `documentId` 应返回 `stale_document`,不能静默操作新页面。 + +### 3.2 授权从两级改为 capability scopes + +原有 `read | control` 太粗。当前状态已经保存具体 scope,UI 的“只读/控制”仅作为创建 scope 集合的快捷预设;Eval 表达式与程序已经拆成独立 scope。 + +建议 scope: + +```text +context.read +cookies.read +storage.read +network.read +page.invoke +page.eval.expression +page.eval.program +page.interact +proxy.read +proxy.write +human.takeover +``` + +授权至少包含: + +```ts +interface BrowserGrant { + id: string; + taskId: string; + agentId?: string; + targets: BrowserTarget[]; + origins: string[]; + scopes: CapabilityScope[]; + createdAt: number; + expiresAt: number; +} +``` + +`page.eval.program` 应独立授权。首次高风险执行应允许用户预览代码和目标 origin。 + +### 3.3 消息协议必须运行时校验 + +TypeScript 类型不会校验来自 runtime、content script、Native Messaging 或 WebSocket 的真实数据。 + +当前已建立严格 request map: + +```ts +interface RequestMap { + 'context.eval': { + input: EvalRequest; + output: PageEvalResult; + }; + 'proxy.switch': { + input: { id: string }; + output: ExtensionState; + }; +} +``` + +配合 Zod、Valibot 或等价 schema 校验: + +- Bridge envelope 和 protocol version; +- `tabId`、`frameId`、`documentId`; +- Eval 代码长度、模式、超时和并发数量; +- Cookie URL、domain、path 和 expiration; +- 代理 host、port、scheme、PAC 数据; +- loopback WebSocket endpoint、配对状态、双方公钥与签名 envelope; +- Grant scope、origin、task 和有效期; +- 单请求和返回值大小。 + +### 3.4 Storage 避免并发覆盖 + +原有状态写入为 `get -> modify -> set`,并发写可能丢失更新。当前已经串行化所有跨域 mutation,按代理、UA、Bridge、面板拆分长期 key,并把 grant/handoff、Bridge session、Agent timeline、代理密码/统计放入 session key;审计和本地聚合指标使用独立有界 key。 + +当前按领域拆 key: + +```text +settings.proxy +settings.userAgent +settings.bridge +ui.floatingPanel +session.activeGrant +session.audit +``` + +写操作由 background 串行执行。临时会话与长期配置分开存储。 + +### 3.5 Bridge 必须有正式握手 + +目标握手: + +```text +engine signed challenge + -> extension verifies paired engine identity + -> extension signed auth (origin + installation + task/grant) + -> engine verifies paired device identity + -> hello_ack + protocol/capability negotiation + -> ready +``` + +在收到 `hello_ack` 之前不能显示“引擎已连接”。 + +Bridge v3 已具备双方 P-256 身份校验、Origin/installation 绑定、protocol/capability/版本协商、request cancel、8 个并发请求上限、16 MiB 总上限和有界事件回程;超过 512 KiB 的消息按 256 KiB 分片。握手携带 installation/task/grant/resume session,回执携带 engine identity/instance/connection/session 身份;心跳记录序号、时间和延迟。断线中的具体调用明确失败,重连恢复逻辑 task/grant session 身份,不伪装恢复已经中断的调用栈。 + +## 4. Eval 发布策略 + +### 4.1 先区分“构建渠道”和“执行机制” + +`store`、`enterprise`、`dev` 是三个发布渠道,不是三个完全独立的 JavaScript 语义。 + +底层执行机制主要有三种: + +1. 当前的 injected MAIN-world bridge; +2. `userScripts.execute({ world: "MAIN" })`; +3. 仅允许预定义的 `page.invoke` / structured commands。 + +推荐矩阵: + +| 构建渠道 | 首选执行机制 | 备用机制 | +| --- | --- | --- | +| Chrome Web Store | User Scripts MAIN | Invoke-only | +| Enterprise managed | User Scripts MAIN | 受管策略允许的 injected bridge | +| Local development | Injected MAIN bridge | User Scripts MAIN 对照测试 | +| Firefox MV3 AMO | Invoke-only / structured commands | 无通用 Eval 回退 | +| Firefox 本地/受管 | Injected bridge | Invoke-only | +| Firefox MV2 | Injected bridge | Invoke-only | + +### 4.2 三种渠道的使用效果是否完全一样 + +结论:目标能力可以接近,但不完全一样。 + +#### A. 当前 injected MAIN-world bridge + +执行链路: + +```text +Bridge/background + -> isolated content script + -> CustomEvent + -> packaged page-main-world.js + -> indirect eval(code) +``` + +优点: + +- 可以访问页面真实 `window`、闭包外全局对象和页面函数; +- 可以等待 Promise; +- 可以自定义循环对象、DOM Node、BigInt 等序列化; +- Chrome、现有 Firefox MV2 构建都可使用; +- 开发时不依赖用户开启 User Scripts 权限。 + +不足: + +- 页面可以观察、修改或干扰 MAIN world 逻辑; +- 页面可以伪造 CustomEvent 响应; +- 请求和返回需要自己维护关联、超时、序列化; +- 同步死循环无法中断; +- 从 Bridge 获取代码再调用 `eval()` 很可能不符合 Chrome Web Store MV3 政策; +- 更适合本地开发、自托管或受管环境,不适合作为公开商店版默认机制。 + +#### B. User Scripts MAIN + +执行链路: + +```text +background + -> browser.userScripts.execute({ + target, + world: "MAIN", + js: [{ code }] + }) + -> browser InjectionResult[] +``` + +与当前方案相同或接近的部分: + +- `world: "MAIN"` 可以访问页面真实 `window` 和页面全局函数; +- 可以执行动态代码; +- 可以指定 tab、frame 或 document; +- 可以等待 Promise; +- 可以返回每个 frame 的执行结果; +- 可以在代码外包一层统一 serializer,保持现有 `PageEvalResult` 格式。 + +统一语义为:`expression` 自动返回表达式值;`program` 是 async 函数体,必须显式 `return` 才产生返回值,否则为 `undefined`。这避免在 Store MAIN world 内二次调用 `eval`,也让 User Scripts 与 injected fallback 的程序行为一致。 + +不同点: + +- Chrome 需要 `userScripts` permission; +- Chrome 138+ 用户必须在扩展详情页开启 “Allow User Scripts”; +- Firefox 技术上提供 `userScripts`,但当前 AMO 政策将其限定为用户脚本管理器;本产品公开 Firefox 包不使用该 API; +- Chrome 的一次性 `userScripts.execute()` 需要 Chrome 135+; +- 当前项目 Firefox 输出是 MV2,不能直接复用 Firefox 的新 MV3 User Scripts 路径; +- Chrome 和 Firefox 的返回值 clone/serialization 细节不同,跨浏览器应主动返回 JSON string 或统一 envelope; +- MAIN world 依旧能被页面观察和干扰,User Scripts 不是可信执行环境; +- User Scripts 的 one-shot injection 与当前常驻事件桥生命周期不同。 + +因此,在支持的浏览器上,以下使用体验可以做到基本一致: + +```text +输入代码 +选择目标标签页/frame +访问页面 window +等待 Promise +得到统一 PageEvalResult +``` + +但权限开启流程、版本覆盖、frame result、错误格式和底层生命周期不会完全相同。 + +特别注意:`world: "USER_SCRIPT"` 不是当前 Eval 的等价替代。它与页面隔离,不能直接读取页面框架、加密库和业务全局变量。Yakit 需要复用页面签名或登录态逻辑时,必须选择 `MAIN`。 + +#### C. Invoke-only / structured commands + +示例: + +```text +page.invoke +dom.query +dom.click +form.fill +network.findRequest +storage.get +``` + +优点: + +- 权限最容易解释; +- 审计和参数脱敏更容易; +- 对 AI 更稳定,减少生成任意代码; +- 更容易通过公开商店审核; +- 可以对每种能力做明确 schema 和测试。 + +不足: + +- 不能等价替代任意 Eval; +- 遇到未知框架、混淆代码、临时加密逻辑时能力受限; +- 需要持续扩充结构化命令。 + +因此 Invoke-only 应该是 Agent 的首选路径,而不是删除 Eval 后的完全替代。 + +### 4.3 推荐的统一 Eval API + +上层不应知道底层是 User Scripts 还是 injected bridge: + +```ts +interface PageExecutionAdapter { + availability(): Promise; + execute(request: PageExecutionRequest): Promise; +} +``` + +请求: + +```ts +interface PageExecutionRequest { + target: BrowserTarget; + mode: 'expression' | 'program'; + code: string; + timeoutMs: number; + maxResultBytes: number; +} +``` + +Adapter: + +```text +ChromeUserScriptsAdapter +FirefoxUserScriptsAdapter +InjectedMainWorldAdapter +InvokeOnlyAdapter +``` + +UI 和 Bridge 始终调用同一个 `page.execute` capability。Adapter 根据构建渠道、浏览器版本、User Scripts 是否开启以及当前 grant 自动选择。 + +### 4.4 Chrome Web Store 风险 + +Chrome MV3 政策明确将以下行为列为常见违规: + +- 使用 `eval()` 执行从远程来源获得的字符串; +- 构建解释器执行从远程来源获得的复杂命令; +- 让扩展完整功能无法从提交代码中被审核者理解。 + +政策明确列出的远程逻辑执行豁免 API 是: + +- Debugger API; +- User Scripts API。 + +官方资料: + +- [Additional Requirements for Manifest V3](https://developer.chrome.com/docs/webstore/program-policies/mv3-requirements) +- [chrome.userScripts](https://developer.chrome.com/docs/extensions/reference/api/userScripts) +- [Enabling chrome.userScripts is changing](https://developer.chrome.com/blog/chrome-userscript) +- [MDN userScripts](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts) +- [MDN userScripts.execute](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts/execute) + +源码预审、实际 Store 包审计、隐私/Limited Use/权限说明和 reviewer test packet 已完成;正式批准仍必须通过 Chrome Web Store 开发者账号上传和人工审核,不能由本地测试替代。 + +## 5. Page Context 目标模型 + +页面上下文已经从“一次返回完整 HTML”切换为有界结构化快照,并完成 frame 与浏览器存储 inventory。 + +建议层次: + +```text +Page summary +Accessibility/DOM action tree +Forms and actionable elements +Frames and shadow roots +Authentication signals +Storage inventory +Network request summary +Crypto/signing observations +Relevant excerpts on demand +``` + +必须补齐: + +- [已完成] main frame、同源 frame、跨源 frame 清单; +- [已完成] `frameId`、`documentId` 和 origin; +- [已完成] open Shadow Root 遍历; +- [已完成] IndexedDB database/store/key 概况; +- [已完成] CacheStorage 概况; +- [已完成] SPA route 和 document 生命周期; +- [已完成] 页面登录状态信号; +- [已完成] 结构化可操作元素引用; +- [已完成] context diff,而不是每次返回完整快照。 + +元素引用建议: + +```text +captureId + documentId + frameId + nodeId +``` + +页面变化后返回 `stale_node`,不能退化为可能误命中的 CSS selector。 + +## 6. 高价值产品功能 + +### 6.1 浏览器请求到 Yakit 工作流 + +浏览器请求到 Yakit 的生产链路已经闭环:`webRequest` 捕获 Fetch/XHR/Form navigation,用户显式开启敏感字段后生成 HTTP/1.1 重放包,并通过带回执的 Bridge 在 Yakit 中打开 Web Fuzzer、生成可运行 Yak PoC,或生成不含认证值的 AI 分析上下文。AI Agent 可结合附近的 WebCrypto/CryptoJS/WebSocket 观测分析鉴权、签名、重放和对象级越权风险。 + +优先完成: + +```text +捕获 fetch / XHR / form 请求 + -> 发送到 Web Fuzzer + -> 发送到 Repeater + -> 生成 Yak PoC + -> 交给 AI 分析鉴权、签名和越权风险 +``` + +这是浏览器插件与 Yakit 结合最直接的产品价值。 + +### 6.2 前端加密与签名观测 + +在明确授权期间临时观测: + +- `fetch` / XHR; +- WebSocket; +- `crypto.subtle`; +- 常见 CryptoJS; +- 请求签名前后的字段; +- 调用栈和脚本来源。 + +上述能力已经通过独立 WXT MAIN-world entrypoint 落地。观测器使用最多 200 条的有界 ring buffer,默认只记录算法、方向、大小、调用栈和脚本来源;短时值预览需要独立敏感 scope,授权到期、撤销或用户停止时恢复原始页面 API 并销毁预览。 + +### 6.3 登录态工作区 + +建立可见 session: + +```text +目标 origin +关联标签页 +账号线索 +Cookie/Storage 概况 +CSRF/token 来源 +当前代理环境 +共享给哪个 Agent/task +授权和过期时间 +最近上下文变化 +``` + +默认不导出原始认证值。快照导出必须显式确认并支持脱敏。 + +### 6.4 人机接管状态机 + +当前已经完成可运行闭环:Agent 创建 `browser.handoff.request`,插件在目标标签页、Popup 和 Options 显示 `waiting_for_user`,用户完成或取消后发送 `browser.handoff.changed`,Yak 通过 `ExtensionWaitEvent` 消费;独立 Agent runtime 同时记录 running/paused/revoked 与有界 action timeline。 + +目标状态: + +```text +agent_running +needs_human +waiting_for_human +human_resumed +agent_resuming +completed / cancelled / expired +``` + +流程: + +1. Agent 说明需要扫码、MFA、CAPTCHA 或设备确认的原因; +2. 扩展聚焦目标标签页并显示任务; +3. Agent 停止读取敏感内容和重复轮询; +4. 用户完成操作并点击“已完成”; +5. 扩展发送 `human_resumed`; +6. Agent 获取新 context diff 并继续。 + +## 7. 现有工具完善方向 + +### Cookie Editor + +- [已完成] 搜索、排序和过滤; +- [已完成] 批量删除; +- [已完成] 编辑现有 Cookie; +- [已完成] JSON/Netscape/Raw Set-Cookie 有界导入导出,默认脱敏,原始值导出需显式开启; +- [已完成] Partitioned Cookie; +- [已完成] SameSite 与过期时间;Priority/SameParty 可在交换格式中识别和展示,浏览器 Cookies API 无法写回时返回明确 warning; +- [已完成] 按 domain/path 分组; +- [已完成] 默认隐藏 value,点击后显示。 + +### User-Agent 与设备身份 + +当前产品明确选择第一种边界:UI 已命名为 “User-Agent 请求头”,只承诺通过 DNR 修改真实网络请求头,不暗示页面 JS、timezone、viewport、touch 或 geolocation 已被完整伪装。 + +完整设备指纹伪装不属于当前插件承诺;如果未来引入,必须作为独立能力重新设计 scope、页面注入生命周期和浏览器兼容测试,不能与单一 UA header 规则混为一谈。 + +### 代理规则 + +- [已完成] 优先级与拖动排序; +- [已完成] 冲突检测; +- [已完成] 当前 URL 命中预览; +- [已完成] PAC 编译结果查看; +- [已完成] 代理认证,用户名持久化、密码仅保存在浏览器 session; +- [已完成] JSON 导入导出,不包含代理密码; +- [已完成] 默认出口和 fail-open/fail-closed 行为; +- [已完成] 规则命中统计。 + +## 8. UI/UX 改进 + +### 8.1 字号 + +已清除 8px/9px 字号;正文、辅助说明、表格和技术元数据按下面基线执行,并由 320/390/桌面截图验证。 + +目标: + +- 工作台正文不低于 12px; +- 辅助说明不低于 11px; +- 表格正文 12px; +- tag、时间戳、技术元数据最低 10px; +- 不再使用 8px。 + +### 8.2 Overview 改为当前任务工作台 + +Overview 已改为任务工作台,第一屏展示: + +```text +当前站点与登录环境 +当前代理和流量状态 +正在共享给哪个 Agent +Agent 最近动作 +需要用户完成的步骤 +抓请求 / 采集上下文 / 发送 Fuzzer +``` + +### 8.3 移动和窄视口导航 + +窄视口使用不换行的横向滚动导航;320px 与 390px E2E 检查 document overflow 和导航标签换行。 + +### 8.4 悬浮面板 + +已完成: + +- 当前站点单独隐藏; +- allowlist/denylist; +- 仅在活动 task 中显示; +- 快捷键展开; +- 页面全屏、演示、视频场景自动收起; +- 与网页边缘控件冲突时调整位置; +- 显示当前 task 和授权风险,而不仅是代理状态。 + +## 9. 目标代码目录 + +```text +src/ + app/background/ + index.ts + entrypoints/ + background.ts + popup/ + options/ + agent.content/ + page-main-world.ts + + features/ + proxy/ + cookies/ + identity/ + page-context/ + page-observation/ + network-capture/ + grants/ + handoff/ + diagnostics/ + agent-runtime/ + engine-bridge/ + floating-panel/ + + platform/ + browser/ + storage/ + messaging/ + policy/ + + protocol/ + components/ui/ + components/brand/ + shared/errors.ts +``` + +原则: + +- WXT background entrypoint 只负责注册并调用 `app/background`; +- 每个高风险 feature 拥有 service,纯编译器/交换器与测试放在 feature 内; +- browser API 通过 platform adapter 隔离; +- background 应用 router 只编排 domain service,不在 entrypoint 内实现浏览器业务; +- UI、Bridge 和测试共享同一份协议 schema。 + +## 10. 测试策略 + +### 单元测试 + +- [已完成] PAC compiler、URL pattern 和冲突优先级; +- [已完成] 无迁移 clean bootstrap、storage 分域和并发写; +- [已完成] Bridge envelope、extension RequestMap 与 managed policy validation; +- [已完成] Grant scope/策略判断与 expression/program Eval serializer; +- [已完成] Cookie URL/脱敏交换与 UA DNR 规则生成。 + +### 协议测试 + +- [已完成] pairing code、engine challenge、extension auth、hello_ack、身份字段和 protocol version mismatch; +- [已完成] read/program scope、origin/tab/frame/document 越权; +- [已完成] timeout/cancel、并发、重复 ID、payload 上限与双向 chunk; +- [已完成] 设备审批/撤销、断线 session 恢复、task 到期/撤销与 Native Host framing; +- [已完成] Chromium `connectNative` -> Go Host -> loopback Yak Bridge -> Bridge v3 challenge/auth/identity/heartbeat 的真实端到端验证(生产包仍为 optional permission,只有不可交互的临时测试副本预授权)。 + +### 浏览器 E2E + +- [已完成] Chrome Store/User Scripts 与 Enterprise User Scripts + injected fallback 模式; +- [已完成] Firefox MV2 injected 与 Firefox MV3 AMO invoke-only 构建/静态策略审计; +- [已完成] CSP 严格页面、SPA、同源/跨源 iframe 与 open Shadow DOM; +- [已完成] 页面伪造消息不扩权、Service Worker 停启保留 session、标签页关闭/导航 Eval fail-closed; +- [已完成] 320px、390px 和桌面视口 UI、面板边界与资源像素/加载检查。 + +当前容器没有 Firefox 可执行程序或 macOS/Windows 环境;Firefox 真机安装、AMO 签名包和三平台 Native Host 签名属于 `RELEASE_CHECKLIST.md` 的外部发布门禁,不能用 Chromium 模拟结果冒充通过。 + +## 11. 分阶段落地 + +### Phase 1:安全与架构基线 + +- [已完成] sender tab/frame/document 绑定与 stale-document; +- [已完成] RequestMap 和运行时 schema; +- [已完成] Storage 分域、session/local 生命周期与串行写; +- [已完成] capability scopes 与 origin 绑定; +- [已完成] Bridge hello_ack 和版本协商; +- [已完成] Store/enterprise/dev 构建渠道; +- [已完成] PageExecutionAdapter; +- [已完成] Chrome User Scripts MAIN,并通过浏览器 E2E。 + +### Phase 2:核心产品闭环 + +- [已完成] Fetch/XHR request capture; +- [已完成] 发送 Yakit Web Fuzzer/Repeater 工作区; +- [已完成] task-bound grant; +- [已完成] human handoff 状态机; +- [已完成] context diff; +- [已完成] Agent action timeline、暂停/恢复/撤销与脱敏持久审计。 + +### Phase 3:浏览器现场深度 + +- [已完成] frame/document/node 引用与显式跨 frame 授权; +- [已完成] open Shadow DOM; +- [已完成] IndexedDB/CacheStorage inventory; +- [已完成] Fetch/XHR/Form/WebSocket/WebCrypto/CryptoJS 有界观测与独立敏感 scope; +- [已完成] 登录态工作区; +- [已完成] Cookie、UA 请求头边界和代理规则完善。 + +### Phase 4:分发与运营 + +- [已完成] Native Host 可执行程序、framing proxy、Chrome/Firefox argv 来源校验、Linux/macOS/Windows 安装器与 Chromium 真实传输 E2E; +- [已完成] managed storage schema、后台强制企业策略与 UI 锁定状态; +- [已完成] Chrome Store 实包自动预审和 reviewer packet;实际上传/批准为外部门禁; +- [已完成] Firefox MV3 AMO invoke-only 实包与 review packet;真机/签名/批准为外部门禁; +- [已完成] 权限说明、隐私政策和 Limited Use 披露; +- [已完成] 脱敏审计、session action timeline 与显式诊断导出; +- [已完成] Service Worker 启动、Bridge 连接错误、心跳延迟和 capability 聚合指标(仅本地,不远传)。 + +## 12. 验收原则 + +正式产品版本至少满足: + +- 不会因 active tab 切换而操作错误页面; +- 每个高风险能力都能追溯到 user、task、grant、target 和 scope; +- 页面不能通过伪造普通消息扩展自己的权限; +- Store build 不通过通用 `eval(remoteCode)` 执行 Bridge 代码; +- User Scripts 未开启时给出明确降级和开启路径; +- Agent 默认使用 structured commands,Eval 是最后手段; +- 用户能看见、暂停、恢复和撤销 Agent 对浏览器的操作; +- 默认不记录或导出 Cookie、token、Eval 参数和页面正文; +- Chrome Store、Enterprise User Scripts 与 Enterprise injected fallback 关键路径有真实 Chromium E2E;Firefox 真机安装/运行是发布前外部门禁,不能由 Chromium 或静态审计替代; +- Native Host 与 Yakit 实例身份、版本和连接状态可信。 diff --git a/docs/store-review/CHROME_WEB_STORE.md b/docs/store-review/CHROME_WEB_STORE.md new file mode 100644 index 0000000..56f6ef0 --- /dev/null +++ b/docs/store-review/CHROME_WEB_STORE.md @@ -0,0 +1,43 @@ +# Chrome Web Store Review Packet + +## Single purpose + +Yakit Browser Agent provides consent-gated browser context and request workflows for authorized security testing with a local Yak/Yakit engine. Cookie, proxy, UA, observation and request tools support that single authenticated-browser testing workflow; they do not provide unrelated browsing, advertising or content features. + +## Remote code policy + +The Store build is produced by `pnpm build:store`. + +- It requires Chrome 138+ and uses the documented `userScripts.execute({ world: "MAIN" })` path. +- `page-main-world.js` is absent from the package and web-accessible resources. +- Expression and program Eval use independent grant scopes; program mode is not in the default control preset. +- If Allow User Scripts is disabled, the UI reports the condition and does not fall back to injected Eval. +- Page results are untrusted and bounded. Structured context/node commands are preferred. + +Chrome's MV3 policy names User Scripts as an API permitted to execute remote logic when used for its documented purpose: [Additional Requirements for Manifest V3](https://developer.chrome.com/docs/webstore/program-policies/mv3-requirements). + +## User data and Limited Use + +The listing and privacy form must disclose authentication information, browsing activity, website content, Cookie/storage data, request data and local Native Messaging transmission. Data is handled only for the user-facing security workflow, sent only to the user's explicit local endpoint, never sold, never used for advertising, and not sent to developer analytics. Local processing still requires disclosure under the [User Data FAQ](https://developer.chrome.com/docs/webstore/program-policies/user-data-faq). + +## Reviewer test + +1. Build with `pnpm build:store` and load `.output/chrome-mv3-store`. +2. Enable Allow User Scripts on the extension details page. +3. Open an HTTP(S) page, Options, and select the target tab. +4. Create a five-minute read grant and verify context succeeds but Eval is denied. +5. Create a control grant. Expression Eval succeeds; program Eval remains denied until separately enabled. +6. Start metadata-only request capture. Headers/body appear only after their explicit switches are enabled. +7. Trigger and complete a handoff; verify the action timeline and audit contain metadata only. +8. Inspect the Store artifact: no `page-main-world.js`, no `activeTab`, and `nativeMessaging` is optional. + +Automated equivalent: `pnpm verify:ui:store`. + +## Submission fields still requiring owner action + +- Developer account ownership and verified contact details. +- Stable privacy-policy URL hosting `docs/PRIVACY_POLICY.md`. +- Final signed extension ID for Native Host allowlisting. +- Store screenshots/promotional assets selected from `.artifacts/ui`. +- Privacy questionnaire answers matching this packet. +- Actual upload, reviewer correspondence and approval. diff --git a/docs/store-review/FIREFOX_AMO.md b/docs/store-review/FIREFOX_AMO.md new file mode 100644 index 0000000..764696a --- /dev/null +++ b/docs/store-review/FIREFOX_AMO.md @@ -0,0 +1,13 @@ +# Firefox AMO Review Packet + +The public Firefox artifact is `pnpm build:firefox:amo`, producing Firefox MV3 in `.output/firefox-mv3-store`. + +Mozilla's current Add-on Policies reserve `userScripts` for user-script managers. Yakit Browser Agent is not marketed as one, so the AMO artifact does not request `userScripts`, does not package `page-main-world.js`, and does not advertise `browser.invoke` or `browser.eval`. It retains structured context, document-bound node commands, request capture, observation, Cookie/UA/proxy tools and human handoff. Local or enterprise Firefox builds can use the injected adapter outside the public AMO channel. + +The manifest targets Firefox 140+ and declares required built-in data consent categories: authentication information, browsing activity, website activity and website content. There is no remote technical/user-interaction telemetry; operational metrics stay local until the user exports a diagnostics file. + +Official references: [Mozilla Add-on Policies](https://extensionworkshop.com/documentation/publish/add-on-policies/) and [Firefox built-in data consent](https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/). + +Reviewer steps mirror the Chrome structured-command flow but must confirm that no function-call/Eval tabs or Bridge capabilities are present. Native Messaging remains optional and any data sent to the local host remains subject to the same disclosure and user controls. + +Owner-only remaining work: AMO account, signed submission, source-code archive if requested, hosted privacy URL, reviewer correspondence and approval. diff --git a/docs/store-review/LIMITED_USE_DISCLOSURE.md b/docs/store-review/LIMITED_USE_DISCLOSURE.md new file mode 100644 index 0000000..0831747 --- /dev/null +++ b/docs/store-review/LIMITED_USE_DISCLOSURE.md @@ -0,0 +1,14 @@ +# Limited Use Disclosure + +Yakit Browser Agent handles browsing activity, website content, authentication information, Cookie/browser-storage data and selected network request data only to provide its prominently disclosed authenticated-browser security-testing features. + +The extension's use of this data complies with the following commitments: + +- Data is used only to display browser context to the user, execute the user's bounded security workflow, or transmit an explicitly granted operation to the user's local Yak/Yakit engine. +- Data is not sold or transferred for advertising, marketing, creditworthiness, lending, or unrelated profiling. +- Humans do not read user data except when the user deliberately includes a redacted diagnostic artifact in a support request, or when required for security, abuse prevention or law. +- There is no developer-operated telemetry endpoint. Aggregate operational metrics remain on device. +- Sensitive request fields and observation values are off by default. Cookie exports are redacted by default. Program Eval has a separate high-risk scope. +- The local Native Host receives only the same purpose-bound messages the user authorized; it is not an independent data collector. + +Store privacy-form answers, listing text and the hosted privacy policy must remain consistent with this disclosure and actual packaged behavior. See the [Chrome Limited Use guidance](https://developer.chrome.com/docs/webstore/user_data) and [Mozilla Add-on Policies](https://extensionworkshop.com/documentation/publish/add-on-policies/). diff --git a/docs/store-review/RELEASE_CHECKLIST.md b/docs/store-review/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..32a6173 --- /dev/null +++ b/docs/store-review/RELEASE_CHECKLIST.md @@ -0,0 +1,28 @@ +# Release Checklist + +## Automated gates + +- `pnpm verify:production` +- `pnpm verify:ui:store` +- `pnpm verify:ui:enterprise` +- `pnpm verify:ui:enterprise:fallback` +- `pnpm verify:native` (real Chromium -> Native Host -> Yak Bridge transport; temporary test copy pre-grants the otherwise optional browser permission) +- `go test ./common/browser/... ./common/ai/aid/aitool/buildinaitools/yakscripttools` in Yak +- Store package has no injected Eval bridge. +- Firefox AMO package has no `page-main-world.js`, `userScripts`, `browser.invoke` or `browser.eval` capability. +- Required permissions match `docs/PERMISSIONS.md`; Native Messaging is optional. +- Windows manifests are written as UTF-8 without BOM and Chrome, Chromium, Edge, Brave and Firefox registrations are per-user. +- Diagnostic and audit fixtures contain no secrets, URLs, request payloads or Eval source. + +## Human gates + +- Review listing text, screenshots and single-purpose statement. +- Host and link the privacy policy. +- Complete Chrome privacy/Limited Use and Firefox data consent declarations. +- Build/sign Native Host binaries for Windows, macOS and Linux; scan and publish checksums. +- Replace unpacked extension IDs in Native Host manifests with signed IDs. +- Test current stable Chrome, Firefox, Windows, macOS and Linux packages on real machines. +- Run `go test ./common/ai/aid/aitool/buildinaitools/...` against a seeded, writable Yakit profile database; the recursive integration package expects existing built-in tools and is not a clean-profile unit test. +- Submit to Chrome Web Store and AMO, answer reviewer questions, and record approval/version IDs. + +The human gates require external accounts, signing keys, store systems and operating systems. They cannot be truthfully marked approved from a source workspace; the repository contains the implementation and reviewer artifacts needed to execute them. diff --git a/native-host/README.md b/native-host/README.md new file mode 100644 index 0000000..3337558 --- /dev/null +++ b/native-host/README.md @@ -0,0 +1,25 @@ +# Yakit Browser Agent Native Host + +The Native Host is a small stdio-to-loopback-WebSocket transport. It does not store pairing credentials and does not execute browser commands itself. It forwards the normal Bridge v3 server challenge and extension authentication messages; Yak validates the origin-bound paired device signature and returns the engine identity, engine instance, connection, session, task, grant, protocol, and capability identity. + +Build from the Yak repository: + +```bash +go build -o yakit-browser-agent-host ./common/browser/nativehostcmd +``` + +Install for Linux or macOS, using the ID shown by `chrome://extensions` for an unpacked build: + +```bash +./native-host/install.sh \ + --host-binary /absolute/path/to/yakit-browser-agent-host \ + --extension-id YOUR_CHROME_EXTENSION_ID +``` + +On Windows, run PowerShell without administrator privileges: + +```powershell +.\native-host\install.ps1 -HostBinary C:\path\yakit-browser-agent-host.exe -ExtensionId YOUR_CHROME_EXTENSION_ID +``` + +The installer registers Chrome, Chromium, Edge, Brave, and Firefox per-user locations. Chrome supplies its extension origin to the host. Firefox supplies the Native Host manifest path and add-on ID; the host verifies that ID against `allowed_extensions` before deriving its Bridge origin. It writes only the loopback endpoint to the user configuration directory. Run with `--uninstall` on POSIX or `-Uninstall` on Windows to remove the registrations; uninstall does not require an extension ID. When Chrome runs on Windows and development runs in WSL, build/install the Windows host with `install.ps1`; a Linux Native Host cannot be launched by Windows Chrome. diff --git a/native-host/install.ps1 b/native-host/install.ps1 new file mode 100644 index 0000000..7c5c6e3 --- /dev/null +++ b/native-host/install.ps1 @@ -0,0 +1,71 @@ +param( + [string]$ExtensionId = "", + [string]$HostBinary = "yakit-browser-agent-host.exe", + [string]$FirefoxId = "browser-agent@yaklang.com", + [string]$Endpoint = "ws://127.0.0.1:64333/extension", + [switch]$Uninstall +) + +$ErrorActionPreference = "Stop" +$Utf8NoBom = New-Object System.Text.UTF8Encoding($false) +$HostName = "com.yaklang.browser_agent" +$InstallRoot = Join-Path $env:LOCALAPPDATA "Yakit\BrowserAgent" +$ConfigRoot = Join-Path $env:APPDATA "yakit" +$ManifestPath = Join-Path $InstallRoot "$HostName.json" +$RegistryTargets = @( + "HKCU:\Software\Google\Chrome\NativeMessagingHosts\$HostName", + "HKCU:\Software\Chromium\NativeMessagingHosts\$HostName", + "HKCU:\Software\Microsoft\Edge\NativeMessagingHosts\$HostName", + "HKCU:\Software\BraveSoftware\Brave-Browser\NativeMessagingHosts\$HostName", + "HKCU:\Software\Mozilla\NativeMessagingHosts\$HostName" +) + +function Write-JsonFile { + param([Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][object]$Value) + [System.IO.File]::WriteAllText($Path, ($Value | ConvertTo-Json -Depth 4), $script:Utf8NoBom) +} + +if ($Uninstall) { + foreach ($Target in $RegistryTargets) { Remove-Item $Target -Recurse -Force -ErrorAction SilentlyContinue } + Remove-Item $InstallRoot -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item (Join-Path $ConfigRoot "browser-agent-native-host.json") -Force -ErrorAction SilentlyContinue + Write-Host "Removed $HostName." + exit 0 +} + +if ($Endpoint -notmatch '^wss?://(127\.0\.0\.1|localhost|\[::1\])(:[0-9]+)?/') { + throw "Endpoint must use ws:// or wss:// with an explicit loopback host." +} +if ([string]::IsNullOrWhiteSpace($ExtensionId)) { + throw "ExtensionId is required when installing the Native Host." +} +$ResolvedBinary = (Resolve-Path $HostBinary).Path +New-Item $InstallRoot -ItemType Directory -Force | Out-Null +New-Item $ConfigRoot -ItemType Directory -Force | Out-Null +$InstalledBinary = Join-Path $InstallRoot "yakit-browser-agent-host.exe" +Copy-Item $ResolvedBinary $InstalledBinary -Force +Write-JsonFile -Path (Join-Path $ConfigRoot "browser-agent-native-host.json") -Value @{ endpoint = $Endpoint } + +Write-JsonFile -Path $ManifestPath -Value @{ + name = $HostName + description = "Yakit Browser Agent Native Host" + path = $InstalledBinary + type = "stdio" + allowed_origins = @("chrome-extension://$ExtensionId/") +} + +$FirefoxManifestPath = Join-Path $InstallRoot "$HostName.firefox.json" +Write-JsonFile -Path $FirefoxManifestPath -Value @{ + name = $HostName + description = "Yakit Browser Agent Native Host" + path = $InstalledBinary + type = "stdio" + allowed_extensions = @($FirefoxId) +} + +foreach ($Target in $RegistryTargets) { + New-Item $Target -Force | Out-Null + $Value = if ($Target -like "*Mozilla*") { $FirefoxManifestPath } else { $ManifestPath } + Set-Item $Target -Value $Value +} +Write-Host "Installed $HostName at $InstalledBinary" diff --git a/native-host/install.sh b/native-host/install.sh new file mode 100755 index 0000000..239ff0f --- /dev/null +++ b/native-host/install.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +set -euo pipefail + +host_name="com.yaklang.browser_agent" +host_binary="" +extension_id="" +firefox_id="browser-agent@yaklang.com" +endpoint="ws://127.0.0.1:64333/extension" +uninstall=false + +usage() { + printf '%s\n' "Usage: $0 --extension-id ID [--host-binary PATH] [--endpoint WS_URL] [--firefox-id ID] [--uninstall]" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --host-binary) host_binary="${2:-}"; shift 2 ;; + --extension-id) extension_id="${2:-}"; shift 2 ;; + --firefox-id) firefox_id="${2:-}"; shift 2 ;; + --endpoint) endpoint="${2:-}"; shift 2 ;; + --uninstall) uninstall=true; shift ;; + -h|--help) usage; exit 0 ;; + *) printf 'Unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;; + esac +done + +case "$(uname -s)" in + Darwin) + install_root="$HOME/Library/Application Support/Yakit/BrowserAgent" + config_root="$HOME/Library/Application Support/yakit" + chrome_roots=( + "$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts" + "$HOME/Library/Application Support/Chromium/NativeMessagingHosts" + "$HOME/Library/Application Support/Microsoft Edge/NativeMessagingHosts" + "$HOME/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts" + ) + firefox_root="$HOME/Library/Application Support/Mozilla/NativeMessagingHosts" + ;; + Linux) + install_root="${XDG_DATA_HOME:-$HOME/.local/share}/yakit/browser-agent" + config_root="${XDG_CONFIG_HOME:-$HOME/.config}/yakit" + chrome_roots=( + "$HOME/.config/google-chrome/NativeMessagingHosts" + "$HOME/.config/chromium/NativeMessagingHosts" + "$HOME/.config/microsoft-edge/NativeMessagingHosts" + "$HOME/.config/BraveSoftware/Brave-Browser/NativeMessagingHosts" + ) + firefox_root="$HOME/.mozilla/native-messaging-hosts" + ;; + *) printf 'Unsupported operating system. Use install.ps1 on Windows.\n' >&2; exit 1 ;; +esac + +manifest_name="$host_name.json" +if [[ "$uninstall" == true ]]; then + for directory in "${chrome_roots[@]}" "$firefox_root"; do rm -f "$directory/$manifest_name"; done + rm -f "$install_root/yakit-browser-agent-host" "$config_root/browser-agent-native-host.json" + printf 'Removed %s manifests and host binary.\n' "$host_name" + exit 0 +fi + +if [[ -z "$extension_id" ]]; then printf '%s\n' '--extension-id is required for Chrome/Chromium.' >&2; exit 2; fi +if [[ -z "$host_binary" ]]; then host_binary="$(command -v yakit-browser-agent-host || true)"; fi +if [[ -z "$host_binary" || ! -x "$host_binary" ]]; then + printf '%s\n' 'Host binary not found. Build it with: go build -o yakit-browser-agent-host ./common/browser/nativehostcmd' >&2 + exit 1 +fi +if [[ ! "$endpoint" =~ ^wss?://(127\.0\.0\.1|localhost|\[::1\])(:[0-9]+)?/ ]]; then + printf '%s\n' 'Endpoint must use ws:// or wss:// with an explicit loopback host.' >&2 + exit 2 +fi + +mkdir -p "$install_root" "$config_root" +install -m 0755 "$host_binary" "$install_root/yakit-browser-agent-host" +printf '{"endpoint":"%s"}\n' "$endpoint" > "$config_root/browser-agent-native-host.json" + +for directory in "${chrome_roots[@]}"; do + mkdir -p "$directory" + printf '{\n "name": "%s",\n "description": "Yakit Browser Agent Native Host",\n "path": "%s",\n "type": "stdio",\n "allowed_origins": ["chrome-extension://%s/"]\n}\n' \ + "$host_name" "$install_root/yakit-browser-agent-host" "$extension_id" > "$directory/$manifest_name" +done + +mkdir -p "$firefox_root" +printf '{\n "name": "%s",\n "description": "Yakit Browser Agent Native Host",\n "path": "%s",\n "type": "stdio",\n "allowed_extensions": ["%s"]\n}\n' \ + "$host_name" "$install_root/yakit-browser-agent-host" "$firefox_id" > "$firefox_root/$manifest_name" + +printf 'Installed %s at %s\n' "$host_name" "$install_root/yakit-browser-agent-host" diff --git a/package.json b/package.json index e4ce3db..4af6320 100644 --- a/package.json +++ b/package.json @@ -2,31 +2,52 @@ "name": "yakit-chrome-client", "description": "Yakit Browser Extension", "private": true, - "version": "0.1.0", + "version": "0.2.0", "type": "module", "scripts": { "dev": "wxt", + "dev:wsl": "node scripts/dev-wsl.mjs", "dev:firefox": "wxt -b firefox", "build": "wxt build", + "build:store": "wxt build -b chrome --mode store", + "build:enterprise": "wxt build -b chrome --mode enterprise", "build:firefox": "wxt build -b firefox", + "build:firefox:amo": "wxt build -b firefox --mv3 --mode store", "zip": "wxt zip", "zip:firefox": "wxt zip -b firefox", "compile": "tsc --noEmit", + "test": "vitest run", + "audit:build": "node scripts/audit-build.mjs", + "verify:production": "pnpm test && pnpm compile && pnpm build:store && pnpm build:enterprise && pnpm build:firefox && pnpm build:firefox:amo && pnpm audit:build", + "verify:ui": "node scripts/verify-ui.mjs", + "verify:ui:store": "EXTENSION_PATH=.output/chrome-mv3-store node scripts/verify-ui.mjs", + "verify:ui:enterprise": "EXTENSION_PATH=.output/chrome-mv3-enterprise node scripts/verify-ui.mjs", + "verify:ui:enterprise:fallback": "ENABLE_USER_SCRIPTS=0 EXTENSION_PATH=.output/chrome-mv3-enterprise node scripts/verify-ui.mjs", + "verify:native": "node scripts/verify-native-host.mjs", "postinstall": "wxt prepare" }, "dependencies": { - "@ant-design/icons": "^6.0.0", - "antd": "^5.24.6", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "uuid": "^11.1.0" + "@radix-ui/react-slot": "^1.3.0", + "@radix-ui/react-switch": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.17", + "@radix-ui/react-tooltip": "^1.2.12", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.24.0", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "uuid": "^14.0.1", + "valibot": "^1.4.2" }, "devDependencies": { - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.0", - "@types/uuid": "^10.0.0", - "@wxt-dev/module-react": "^1.1.3", - "typescript": "^5.8.3", - "wxt": "^0.20.0" + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@types/ws": "^8.18.1", + "@wxt-dev/module-react": "^1.2.2", + "playwright-core": "^1.61.1", + "typescript": "^7.0.2", + "vitest": "^4.1.10", + "ws": "^8.21.1", + "wxt": "^0.20.27" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58f149b..1d2be56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,40 +8,67 @@ importers: .: dependencies: - '@ant-design/icons': - specifier: ^6.0.0 - version: 6.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - antd: - specifier: ^5.24.6 - version: 5.24.6(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-switch': + specifier: ^1.3.3 + version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': + specifier: ^1.1.17 + version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': + specifier: ^1.2.12 + version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^1.24.0 + version: 1.24.0(react@19.2.7) react: - specifier: ^18.2.0 - version: 18.3.1 + specifier: ^19.2.7 + version: 19.2.7 react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + specifier: ^19.2.7 + version: 19.2.7(react@19.2.7) uuid: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^14.0.1 + version: 14.0.1 + valibot: + specifier: ^1.4.2 + version: 1.4.2(typescript@7.0.2) devDependencies: '@types/react': - specifier: ^18.2.0 - version: 18.3.20 + specifier: ^19.2.17 + version: 19.2.17 '@types/react-dom': - specifier: ^18.2.0 - version: 18.3.6(@types/react@18.3.20) - '@types/uuid': - specifier: ^10.0.0 - version: 10.0.0 + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 '@wxt-dev/module-react': - specifier: ^1.1.3 - version: 1.1.3(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2))(wxt@0.20.0(@types/node@22.14.1)(jiti@2.4.2)(rollup@4.40.0)) + specifier: ^1.2.2 + version: 1.2.2(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0))(wxt@0.20.27(@types/node@22.14.1)(jiti@2.7.0)(rollup@4.40.0)) + playwright-core: + specifier: ^1.61.1 + version: 1.61.1 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^7.0.2 + version: 7.0.2 + vitest: + specifier: ^4.1.10 + version: 4.1.10(@types/node@22.14.1)(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0)) + ws: + specifier: ^8.21.1 + version: 8.21.1 wxt: - specifier: ^0.20.0 - version: 0.20.0(@types/node@22.14.1)(jiti@2.4.2)(rollup@4.40.0) + specifier: ^0.20.27 + version: 0.20.27(@types/node@22.14.1)(jiti@2.7.0)(rollup@4.40.0) packages: @@ -58,143 +85,29 @@ packages: rollup: optional: true - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@ant-design/colors@7.2.0': - resolution: {integrity: sha512-bjTObSnZ9C/O8MB/B4OUtd/q9COomuJAR2SYfhxLyHvCKn4EKwCN3e+fWGMo7H5InAyV0wL17jdE9ALrdOW/6A==} - - '@ant-design/colors@8.0.0': - resolution: {integrity: sha512-6YzkKCw30EI/E9kHOIXsQDHmMvTllT8STzjMb4K2qzit33RW2pqCJP0sk+hidBntXxE+Vz4n1+RvCTfBw6OErw==} - - '@ant-design/cssinjs-utils@1.1.3': - resolution: {integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@ant-design/cssinjs@1.23.0': - resolution: {integrity: sha512-7GAg9bD/iC9ikWatU9ym+P9ugJhi/WbsTWzcKN6T4gU0aehsprtke1UAaaSxxkjjmkJb3llet/rbUSLPgwlY4w==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/fast-color@2.0.6': - resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} - engines: {node: '>=8.x'} - - '@ant-design/fast-color@3.0.0': - resolution: {integrity: sha512-eqvpP7xEDm2S7dUzl5srEQCBTXZMmY3ekf97zI+M2DHOYyKdJGH0qua0JACHTqbkRnD/KHFQP9J1uMJ/XWVzzA==} - engines: {node: '>=8.x'} - - '@ant-design/icons-svg@4.4.2': - resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} - - '@ant-design/icons@5.6.1': - resolution: {integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==} - engines: {node: '>=8'} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/icons@6.0.0': - resolution: {integrity: sha512-o0aCCAlHc1o4CQcapAwWzHeaW2x9F49g7P3IDtvtNXgHowtRWYb7kiubt8sQPFvfVIVU/jLw2hzeSlNt0FU+Uw==} - engines: {node: '>=8'} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/react-slick@1.1.2': - resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} - peerDependencies: - react: '>=16.9.0' - '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.10': - resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.0': - resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.27.0': - resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.26.5': - resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.27.0': - resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.27.0': - resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-transform-react-jsx-self@7.25.9': - resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.25.9': - resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/runtime@7.24.7': - resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} + '@babel/runtime@7.28.2': + resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.27.0': - resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.27.0': - resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.27.0': - resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@devicefarmer/adbkit-logcat@2.1.3': @@ -205,196 +118,205 @@ packages: resolution: {integrity: sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==} engines: {node: '>= 0.10.4'} - '@devicefarmer/adbkit@3.2.6': - resolution: {integrity: sha512-8lO1hSeTgtxcOHhp4tTWq/JaOysp5KNbbyFoxNEBnwkCDZu/Bji3ZfOaG++Riv9jN6c9bgdLBOZqJTC5VJPRKQ==} + '@devicefarmer/adbkit@3.3.8': + resolution: {integrity: sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==} engines: {node: '>= 0.10.4'} hasBin: true - '@emotion/hash@0.8.0': - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} - '@emotion/unitless@0.7.5': - resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} - '@esbuild/aix-ppc64@0.25.2': - resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.2': - resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.2': - resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.2': - resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.2': - resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.2': - resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.2': - resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.2': - resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.2': - resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.2': - resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.2': - resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.2': - resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.2': - resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.2': - resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.2': - resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.2': - resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.2': - resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.2': - resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.2': - resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.2': - resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.2': - resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.2': - resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.2': - resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.2': - resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.2': - resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 - '@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'} + '@oxc-project/types@0.139.0': + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} @@ -408,66 +330,382 @@ packages: resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} - '@rc-component/async-validator@5.0.4': - resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} - engines: {node: '>=14.x'} + '@radix-ui/primitive@1.1.5': + resolution: {integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==} - '@rc-component/color-picker@2.0.1': - resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@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 - '@rc-component/context@1.4.0': - resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==} + '@radix-ui/react-collection@1.1.12': + resolution: {integrity: sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@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 - '@rc-component/mini-decimal@1.1.0': - resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} - engines: {node: '>=8.x'} - - '@rc-component/mutate-observer@1.1.0': - resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==} - engines: {node: '>=8.x'} + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - '@rc-component/portal@1.1.2': - resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} - engines: {node: '>=8.x'} + '@radix-ui/react-context@1.2.0': + resolution: {integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - '@rc-component/qrcode@1.0.0': - resolution: {integrity: sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==} - engines: {node: '>=8.x'} + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - '@rc-component/tour@1.15.1': - resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==} - engines: {node: '>=8.x'} + '@radix-ui/react-dismissable-layer@1.1.15': + resolution: {integrity: sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@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 - '@rc-component/trigger@2.2.6': - resolution: {integrity: sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q==} - engines: {node: '>=8.x'} + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - '@rc-component/util@1.2.1': - resolution: {integrity: sha512-AUVu6jO+lWjQnUOOECwu8iR0EdElQgWW5NBv5vP/Uf9dWbAX3udhMutRlkVXjuac2E40ghkFy+ve00mc/3Fymg==} + '@radix-ui/react-popper@1.3.3': + resolution: {integrity: sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==} peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' + '@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.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + 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.7': + resolution: {integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==} + 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.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + 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-roving-focus@1.1.15': + resolution: {integrity: sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==} + 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.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.3': + resolution: {integrity: sha512-1+mlB4/lxJfk5tgJ4g+R5mUCbRpPE1T9+UsEyeLYbGgMtwiMgmuTnfKz4Mw1nHALHjuwyxw4MLd4cSHn6pNSlQ==} + 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-tabs@1.1.17': + resolution: {integrity: sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ==} + 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-tooltip@1.2.12': + resolution: {integrity: sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==} + 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.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + 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.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + 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.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + 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-is-hydrated@0.1.1': + resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} + 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.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + 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-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} + 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.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + 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.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + 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.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + 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.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} '@rollup/rollup-android-arm-eabi@4.40.0': resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} @@ -580,25 +818,17 @@ packages: cpu: [x64] os: [win32] - '@sindresorhus/is@5.6.0': - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.20.7': - resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} @@ -612,60 +842,211 @@ packages: '@types/har-format@1.2.16': resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - '@types/minimatch@3.0.5': resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} '@types/node@22.14.1': resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} - '@types/prop-types@15.7.14': - resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - - '@types/react-dom@18.3.6': - resolution: {integrity: sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - '@types/react': ^18.0.0 + '@types/react': ^19.2.0 - '@types/react@18.3.20': - resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} - '@types/uuid@10.0.0': - resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + '@types/webextension-polyfill@0.12.5': + resolution: {integrity: sha512-uKSAv6LgcVdINmxXMKBuVIcg/2m5JZugoZO8x20g7j2bXJkPIl/lVGQcDlbV+aXAiTyXT2RA5U5mI4IGCDMQeg==} - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@vitejs/plugin-react@4.3.4': - resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} - engines: {node: ^14.18.0 || >=16.0.0} + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@vitejs/plugin-react@6.0.3': + resolution: {integrity: sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true - '@webext-core/fake-browser@1.3.2': - resolution: {integrity: sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A==} + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@webext-core/isolated-element@1.1.2': - resolution: {integrity: sha512-CNHYhsIR8TPkPb+4yqTIuzaGnVn/Fshev5fyoPW+/8Cyc93tJbCjP9PC1XSK6fDWu+xASdPHLZaoa2nWAYoxeQ==} + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + + '@webext-core/fake-browser@1.5.2': + resolution: {integrity: sha512-nkDQwOJ23X5Q7cEtN6LRuBtVFf1KVOFi5GoQAro0lzqdh59F5E+K350j1isbnqYbzsXRh1NJtboudIcHfZtvOQ==} + + '@webext-core/isolated-element@1.1.5': + resolution: {integrity: sha512-4m6oP8Vzm/68YO1QmkUOZqqUcmyBtA53tji2g00/nYXE3E3IceYgeub7eIqvXDV2Z7xU6cm6qO1IMt4XFVwtvQ==} '@webext-core/match-patterns@1.0.3': resolution: {integrity: sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A==} - '@wxt-dev/browser@0.0.310': - resolution: {integrity: sha512-0uQlrxUmbEczWFo2KGFTmJlQWpODqMDQOmQmIQGjQiiDs2aE4J6EsVmtmSSCXGMMYQ+jvNR+azf689xOWo0JGw==} + '@wxt-dev/browser@0.2.2': + resolution: {integrity: sha512-QqLOdEE1UQxieRuMbv9rwczD+xUv45fy+i5gw1eo+/vlPtGX+/rBd6tlIfLFCU3xAN/UTH57bxqOeU2IZg7dEg==} - '@wxt-dev/module-react@1.1.3': - resolution: {integrity: sha512-ede2FLS3sdJwtyI61jvY1UiF194ouv3wxm+fCYjfP4FfvoXQbif8UuusYBC0KSa/L2AL9Cfa/lEvsdNYrKFUaA==} + '@wxt-dev/module-react@1.2.2': + resolution: {integrity: sha512-+lRLi1r9dAXpLySWSIWHLJ1h/nFzR20iQnx3RNrKyA6oJg4+ClOluVXozHjfPg9Okfy/umtffiOopGayASrg6w==} peerDependencies: + vite: ^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0 wxt: '>=0.19.16' '@wxt-dev/storage@1.1.1': resolution: {integrity: sha512-H1vYWeoWz03INV4r+sLYDFil88b3rgMMfgGp/EXy3bLbveJeiMiFs/G0bsBN2Ra87Iqlf2oVYRb/ABQpAugbew==} - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} engines: {node: '>=0.4.0'} hasBin: true @@ -688,6 +1069,10 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} + 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'} @@ -696,14 +1081,9 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - antd@5.24.6: - resolution: {integrity: sha512-xIlTa/1CTbgkZsdU/dOXkYvJXb9VoiMwsaCzpKFH2zAEY3xqOfwQ57/DdG7lAdrWP7QORtSld4UA6suxzuTHXw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} array-differ@4.0.0: resolution: {integrity: sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==} @@ -713,84 +1093,54 @@ packages: resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} engines: {node: '>=12'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + atomically@2.1.1: + resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - big-integer@1.6.52: - resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} - engines: {node: '>=0.6'} - - bl@5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + boolbase@2.0.0: + resolution: {integrity: sha512-DkVaaQHymRhpYEYo9x1oo7Q7B0Y6KJUsjm3c9eTyFDby4MHLBTwZ6ZDWBel5zrYxj1WsZgC5oLpiz+93MluXeA==} + engines: {node: '>=20.19.0'} - boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} - - bplist-parser@0.2.0: - resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} - engines: {node: '>= 5.10.0'} + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - bundle-name@3.0.0: - resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} - engines: {node: '>=12'} - bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} - bunyan@1.8.15: - resolution: {integrity: sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==} - engines: {'0': node >=0.10.0} - hasBin: true - - c12@3.0.3: - resolution: {integrity: sha512-uC3MacKBb0Z15o5QWCHvHWj5Zv34pGQj9P+iXKSpTuSGFS0KKhUWf4t9AJ+gWjYOdmWCPEGpEzm8sS0iqbpo1w==} + c12@3.3.4: + resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} peerDependencies: - magicast: ^0.3.5 + magicast: '*' peerDependenciesMeta: magicast: optional: true @@ -799,87 +1149,63 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} - camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - - caniuse-lite@1.0.30001713: - resolution: {integrity: sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} chalk@5.4.1: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} - chrome-launcher@1.1.0: - resolution: {integrity: sha512-rJYWeEAERwWIr3c3mEVXwNiODPEdMRlRxHc47B1qHPOolHZnkj7rMv1QSUfPoG6MgatWj5AxSpnKKR4QEwEQIQ==} + chrome-launcher@1.2.0: + resolution: {integrity: sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==} engines: {node: '>=12.13.0'} hasBin: true - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - ci-info@4.2.0: - resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + citty@0.2.2: + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} cli-boxes@3.0.0: resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} engines: {node: '>=10'} - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} - cli-highlight@2.1.11: - resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} - engines: {node: '>=8.0.0', npm: '>=5.0.0'} - hasBin: true - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} - - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -888,9 +1214,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - commander@2.9.0: resolution: {integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==} engines: {node: '>= 0.6.x'} @@ -899,9 +1222,6 @@ packages: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} - compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -915,12 +1235,15 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} + configstore@7.1.0: + resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==} + engines: {node: '>=18'} consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} @@ -929,35 +1252,22 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - copy-to-clipboard@3.3.3: - resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} + css-select@7.0.0: + resolution: {integrity: sha512-snmjEVXy+1LnwXdxhYvTMj1d9tOh4HxkA1YmoayVBeeyR2C14Pum7fcxJIm4SswYspVy866eYNwlH6xC3/VH5g==} + engines: {node: '>=20.19.0'} - crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} - - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} - - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} + css-what@8.0.0: + resolution: {integrity: sha512-DH0Bqq3DNp5tdOReuNyAA+Ev4Y2GS5FMbZpeTLP6C4CDi0h5nL0BmUPChXw3o/qbHLDWHl49sbNqQVY7bMSDdw==} + engines: {node: '>=20.19.0'} cssom@0.5.0: resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -979,46 +1289,18 @@ packages: supports-color: optional: true - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - default-browser-id@3.0.0: - resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} - engines: {node: '>=12'} - default-browser-id@5.0.0: resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} engines: {node: '>=18'} - default-browser@4.0.0: - resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} - engines: {node: '>=14.16'} - - default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} @@ -1030,6 +1312,9 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1037,40 +1322,57 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-serializer@3.1.1: + resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==} + engines: {node: '>=20.19.0'} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domelementtype@3.0.0: + resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==} + engines: {node: '>=20.19.0'} + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + domhandler@6.0.1: + resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==} + engines: {node: '>=20.19.0'} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} + domutils@4.0.2: + resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==} + engines: {node: '>=20.19.0'} - dotenv-expand@12.0.1: - resolution: {integrity: sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==} + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + + dotenv-expand@12.0.3: + resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} engines: {node: '>=12'} dotenv@16.5.0: resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} - dtrace-provider@0.8.8: - resolution: {integrity: sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==} - engines: {node: '>=0.10'} + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.5.136: - resolution: {integrity: sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -1078,20 +1380,18 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.0: - resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -1099,14 +1399,14 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - esbuild@0.25.2: - resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} hasBin: true @@ -1129,79 +1429,53 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} exsolve@1.0.4: resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - - fdir@6.4.3: - resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true - filesize@10.1.6: - resolution: {integrity: sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==} - engines: {node: '>= 10.4.0'} + filesize@11.0.22: + resolution: {integrity: sha512-RlCVs9CY+oSsRnNZn95J9vDXjNjOwddKyTFjOYtA4yxYVIxBnwiVVGJX+TFhsmu3uUf81JDGyijtYL9xgawlTw==} + engines: {node: '>= 10.8.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - firefox-profile@4.6.0: - resolution: {integrity: sha512-I9rAm1w8U3CdhgO4EzTJsCvgcbvynZn9lOySkZf78wUdUIQH2w9QOKf3pAX+THt2XMSSR3kJSuM8P7bYux9j8g==} + firefox-profile@4.7.0: + resolution: {integrity: sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==} + engines: {node: '>=18'} hasBin: true - form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} + form-data-encoder@4.1.0: + resolution: {integrity: sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==} + engines: {node: '>= 18'} formdata-node@6.0.3: resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==} engines: {node: '>= 18'} - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} - fs-extra@9.0.1: - resolution: {integrity: sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==} - engines: {node: '>=10'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1211,10 +1485,6 @@ packages: resolution: {integrity: sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==} hasBin: true - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1223,47 +1493,27 @@ packages: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} - get-port-please@3.1.2: - resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} + get-port-please@3.2.0: + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} giget@2.0.0: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + giget@3.3.0: + resolution: {integrity: sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==} + hasBin: true glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@6.0.4: - resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} - deprecated: Glob versions prior to v9 are no longer supported - - global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -1277,65 +1527,20 @@ packages: growly@1.3.0: resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==} - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-yarn@3.0.0: - resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - - hookable@5.5.3: - resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} - htmlparser2@10.0.0: - resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - - http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} - - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - - import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - - 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. + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -1343,9 +1548,13 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} is-absolute@0.1.7: resolution: {integrity: sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==} @@ -1354,10 +1563,6 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -1368,54 +1573,43 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true - 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-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - is-fullwidth-code-point@5.0.0: resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} engines: {node: '>=18'} - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + engines: {node: '>=20'} is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} hasBin: true - is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} is-plain-object@2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} @@ -1432,37 +1626,14 @@ packages: resolution: {integrity: sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==} engines: {node: '>=0.10.0'} - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} - is-yarn-global@0.4.1: - resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} - engines: {node: '>=12'} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -1476,8 +1647,8 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true js-tokens@4.0.0: @@ -1486,21 +1657,10 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - 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@3.0.2: resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - json2mq@0.2.0: - resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -1509,19 +1669,30 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} + jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - latest-version@7.0.0: - resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} - engines: {node: '>=14.16'} + ky@1.14.3: + resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==} + engines: {node: '>=18'} + + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -1529,61 +1700,142 @@ packages: lighthouse-logger@2.0.1: resolution: {integrity: sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lines-and-columns@2.0.4: resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - linkedom@0.18.9: - resolution: {integrity: sha512-Pfvhwjs46nBrcQdauQjMXDJZqj6VwN7KStT84xQqmIgD9bPH6UVJ/ESW8y4VHVF2h7di0/P+f4Iln4U5emRcmg==} + linkedom@0.18.13: + resolution: {integrity: sha512-ES/o9qotMpzpN2MHs+Iq/JcVoOj8Fa5wiQYrTdFpvAnwXL0g66XHHUc9WUMk6nAlBtGsFQ24ne+SYnvnaQ2FSw==} + engines: {node: '>=16'} + peerDependencies: + canvas: '>= 2' + peerDependenciesMeta: + canvas: + optional: true - listr2@8.3.2: - resolution: {integrity: sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==} - engines: {node: '>=18.0.0'} + listr2@10.2.2: + resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} + engines: {node: '>=22.13.0'} local-pkg@1.1.1: resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} engines: {node: '>=14'} - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - - log-symbols@5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - - log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} - engines: {node: '>=18'} + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@1.24.0: + resolution: {integrity: sha512-YT6mBD8lGKkg4nM39enlm94/sfJIiW0YKUT60fBy4YK8tai31ylg1VhGNWxkpSKHo9UagfnZqwIff3HTDQwXeA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -1594,62 +1846,19 @@ packages: marky@1.3.0: resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} - moment@2.30.1: - resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} - ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -1660,32 +1869,24 @@ packages: resolution: {integrity: sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - mv@2.1.1: - resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} - engines: {node: '>=0.8.0'} + nano-spawn@2.1.0: + resolution: {integrity: sha512-yTW+2okrElHiH4fsiz/+/zc0EDo9BDDoC3iKk8dpv1GeRc9nUWzUZHx6TofMWErchhUQR8hY9/Eu1Uja9x1nqA==} + engines: {node: '>=20.17'} - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - - nan@2.22.2: - resolution: {integrity: sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==} - - nano-spawn@0.2.0: - resolution: {integrity: sha512-IjZBIOLxSlxu+m/kacg9JuP93oUpRemeV0mEuCy64nzBKKIL9m0aLJHtVPcVuzJDHFhElzjpwbW4a3tMzgKoZQ==} - engines: {node: '>=18.19'} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - ncp@2.0.0: - resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} - hasBin: true + nanospinner@1.2.2: + resolution: {integrity: sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA==} node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} @@ -1693,97 +1894,52 @@ packages: node-notifier@10.0.1: resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} + nth-check@3.0.1: + resolution: {integrity: sha512-GX0gsdbGVCgnRgbeGaubfjpBXyYRWOOCVeYh08bSQvDZqxz5ndXs1OTfAt/h36G1xvI94YIspsI0sVFqAV9+RQ==} + engines: {node: '>=20.19.0'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - nypm@0.3.12: - resolution: {integrity: sha512-D3pzNDWIvgA+7IORhD/IuWzEk4uXv6GsgOxiid4UU3h9oq5IqV1KtPDi63n4sZJ/xcWlr88c0QM2RgN5VbOhFA==} - engines: {node: ^14.16.0 || >=16.10.0} + nypm@0.6.8: + resolution: {integrity: sha512-Q9K4Diu6l5u6xJQogeFSs/zKtyMSgFKFtRQV+tHP4kL7KPm2grpBU0dFIwFaXwNxN0MtfKWc43VpCugAa+LPsw==} + engines: {node: '>=18'} hasBin: true - nypm@0.6.0: - resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} - engines: {node: ^14.16.0 || >=16.10.0} - hasBin: true + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - ofetch@1.4.1: - resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} - - ohash@1.1.6: - resolution: {integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==} + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} - engines: {node: '>=18'} + open@11.0.0: + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + engines: {node: '>=20'} open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} - open@9.1.0: - resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} - engines: {node: '>=14.16'} - - ora@6.3.1: - resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} - os-shim@0.1.3: resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} engines: {node: '>= 0.4.0'} - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - - package-json@8.1.1: - resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} - engines: {node: '>=14.16'} + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -1792,38 +1948,11 @@ packages: resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} engines: {node: '>=16'} - parse5-htmlparser2-tree-adapter@6.0.1: - resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} - - parse5@5.1.1: - resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} - - parse5@6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - - 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'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1832,23 +1961,48 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@9.7.0: + resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==} + hasBin: true + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} pkg-types@2.1.0: resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} + engines: {node: '>=18'} + hasBin: true + + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} engines: {node: ^10 || ^12 || >=14} + powershell-utils@0.1.0: + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + engines: {node: '>=20'} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + promise-toolbox@0.21.0: resolution: {integrity: sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==} engines: {node: '>=6'} @@ -1860,14 +2014,11 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - publish-browser-extension@3.0.0: - resolution: {integrity: sha512-gwjH8mIepNqID2VqKIxzT6lmtvkcc5tcWYzrGSUdkeUFFFSHhGp9xx01EZ7j8wPq50dDe0XU5VNbHMAqr6wWAA==} - engines: {node: ^18.0.0 || >=20.0.0} + publish-browser-extension@4.0.5: + resolution: {integrity: sha512-EePAn3VIHJS/jqCuvs1NgPgoecCT8+RsES76hbgYe2Ze1dyvB0tX60C1PCrV8Z8fv56mW3E59s9Gd/GwWiw7dw==} + engines: {node: '>=18.0.0'} hasBin: true - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} - pupa@3.1.0: resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} engines: {node: '>=12.20'} @@ -1875,277 +2026,35 @@ packages: quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - rc-cascader@3.33.1: - resolution: {integrity: sha512-Kyl4EJ7ZfCBuidmZVieegcbFw0RcU5bHHSbtEdmuLYd0fYHCAiYKZ6zon7fWAVyC6rWWOOib0XKdTSf7ElC9rg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-checkbox@3.5.0: - resolution: {integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-collapse@3.9.0: - resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==} - 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-drawer@7.2.0: - resolution: {integrity: sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-dropdown@4.2.1: - resolution: {integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==} - peerDependencies: - react: '>=16.11.0' - react-dom: '>=16.11.0' - - rc-field-form@2.7.0: - resolution: {integrity: sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-image@7.11.1: - resolution: {integrity: sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input-number@9.4.0: - resolution: {integrity: sha512-Tiy4DcXcFXAf9wDhN8aUAyMeCLHJUHA/VA/t7Hj8ZEx5ETvxG7MArDOSE6psbiSCo+vJPm4E3fGN710ITVn6GA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input@1.7.3: - resolution: {integrity: sha512-A5w4egJq8+4JzlQ55FfQjDnPvOaAbzwC3VLOAdOytyek3TboSOP9qxN+Gifup+shVXfvecBLBbWBpWxmk02SWQ==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-mentions@2.19.1: - resolution: {integrity: sha512-KK3bAc/bPFI993J3necmaMXD2reZTzytZdlTvkeBbp50IGH1BDPDvxLdHDUrpQx2b2TGaVJsn+86BvYa03kGqA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.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-notification@5.6.3: - resolution: {integrity: sha512-42szwnn8VYQoT6GnjO00i1iwqV9D1TTMvxObWsuLwgl0TsOokzhkYiufdtQBsJMFjJravS1hfDKVMHLKLcPE4g==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-overflow@1.4.1: - resolution: {integrity: sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-pagination@5.1.0: - resolution: {integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-picker@4.11.3: - resolution: {integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==} - engines: {node: '>=8.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-progress@4.0.0: - resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-rate@2.13.1: - resolution: {integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==} - engines: {node: '>=8.x'} - 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-segmented@2.7.0: - resolution: {integrity: sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-select@14.16.6: - resolution: {integrity: sha512-YPMtRPqfZWOm2XGTbx5/YVr1HT0vn//8QS77At0Gjb3Lv+Lbut0IORJPKLWu1hQ3u4GsA0SrDzs7nI8JG7Zmyg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '*' - react-dom: '*' - - rc-slider@11.1.8: - resolution: {integrity: sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-steps@6.0.1: - resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-switch@4.1.0: - resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-table@7.50.4: - resolution: {integrity: sha512-Y+YuncnQqoS5e7yHvfvlv8BmCvwDYDX/2VixTBEhkMDk9itS9aBINp4nhzXFKiBP/frG4w0pS9d9Rgisl0T1Bw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tabs@15.5.2: - resolution: {integrity: sha512-Hbqf2IV6k/jPgfMjPtIDmPV0D0C9c/fN4B/fYcoh9qqaUzUZQoK0PYzsV3UaV+3UsmyoYt48p74m/HkLhGTw+w==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-textarea@1.9.0: - resolution: {integrity: sha512-dQW/Bc/MriPBTugj2Kx9PMS5eXCCGn2cxoIaichjbNvOiARlaHdI99j4DTxLl/V8+PIfW06uFy7kjfUIDDKyxQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tooltip@6.4.0: - resolution: {integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tree-select@5.27.0: - resolution: {integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==} - peerDependencies: - react: '*' - react-dom: '*' - - rc-tree@5.13.1: - resolution: {integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==} - engines: {node: '>=10.x'} - peerDependencies: - react: '*' - react-dom: '*' - - rc-upload@4.8.1: - resolution: {integrity: sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw==} - 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' - - rc-virtual-list@3.18.5: - resolution: {integrity: sha512-1FuxVSxhzTj3y8k5xMPbhXCB0t2TOiI3Tq+qE2Bu+GGV7f+ECVuQl4OUg6lZ2qT5fordTW7CBpr9czdzXCI7Pg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc9@2.1.2: - resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + rc9@3.0.1: + resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==} rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: - react: ^18.3.1 + react: ^19.2.7 - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react-refresh@0.14.2: - resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} - engines: {node: '>=0.10.0'} - - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} registry-auth-token@5.1.0: resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} @@ -2159,34 +2068,16 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} - - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@2.4.5: - resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} - deprecated: Rimraf versions prior to v4 are no longer supported + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true rollup@4.40.0: @@ -2194,46 +2085,29 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-applescript@5.0.0: - resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} - engines: {node: '>=12'} - run-applescript@7.0.0: resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} engines: {node: '>=18'} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-json-stringify@1.2.0: - resolution: {integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - - scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} - semver-diff@4.0.0: - resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} - engines: {node: '>=12'} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} @@ -2246,22 +2120,14 @@ packages: setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - 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'} - shell-quote@1.7.3: resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==} shellwords@0.1.1: resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} @@ -2270,14 +2136,17 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - slice-ansi@7.1.0: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2296,38 +2165,34 @@ packages: spawn-sync@1.0.15: resolution: {integrity: sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} - stdin-discarder@0.1.0: - resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - - string-convert@0.2.1: - resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} 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'} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2336,96 +2201,73 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-bom@5.0.0: - resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + strip-bom@5.0.0: + resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==} engines: {node: '>=12'} strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - strip-json-comments@5.0.1: - resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + strip-json-comments@5.0.2: + resolution: {integrity: sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==} engines: {node: '>=14.16'} strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} - 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'} + thread-stream@3.2.0: + resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyglobby@0.2.12: - resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - titleize@3.0.0: - resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} - engines: {node: '>=12'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} - tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toggle-selection@1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - type-fest@3.13.1: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} hasBin: true ufo@1.6.1: @@ -2441,14 +2283,6 @@ packages: resolution: {integrity: sha512-mYVtA0nmzrysnYnyb3ALMbByJ+Maosee2+WyE0puXl+Xm2bUwPorPaaeZt0ETfuroPOtG8jj1g/qeFZ6buFnag==} engines: {node: '>=18.12.0'} - unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} - - universalify@1.0.0: - resolution: {integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==} - engines: {node: '>= 10.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -2461,61 +2295,62 @@ packages: resolution: {integrity: sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w==} engines: {node: '>=18.12.0'} - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - update-notifier@6.0.2: - resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} - engines: {node: '>=14.16'} + update-notifier@7.3.1: + resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} + engines: {node: '>=18'} 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==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - vite-node@3.1.1: - resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + valibot@1.4.2: + resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + + vite-node@6.0.0: + resolution: {integrity: sha512-oj4PVrT+pDh6GYf5wfUXkcZyekYS8kKPfLPXVl8qe324Ec6l4K2DUKNadRbZ3LQl0qGcDz+PyOo7ZAh00Y+JjQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - vite@6.2.6: - resolution: {integrity: sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@8.1.5: + resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -2531,20 +2366,61 @@ packages: yaml: optional: true - watchpack@2.4.1: - resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - web-ext-run@0.2.2: - resolution: {integrity: sha512-GD59q5/1wYQJXTHrljMZaBa3cCz+Jj3FMDLYgKyAa34TPcHSuMaGqp7TcLJ66PCe43C3hmbEAZd8QCpAB34eiw==} + web-ext-run@0.2.4: + resolution: {integrity: sha512-rQicL7OwuqWdQWI33JkSXKcp7cuv1mJG8u3jRQwx/8aDsmhbTHs9ZRmNYOL+LX0wX8edIEQX8jj4bB60GoXtKA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + when@3.7.7: resolution: {integrity: sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==} @@ -2557,33 +2433,32 @@ packages: engines: {node: '>= 8'} hasBin: true - widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} winreg@0.0.12: resolution: {integrity: sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==} + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + 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'} - wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + ws@8.21.1: + resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2594,16 +2469,26 @@ packages: utf-8-validate: optional: true - wxt@0.20.0: - resolution: {integrity: sha512-mu7zP/WlDwBfJ1ys9SPhgbu2vTdd0ulSXpHrkOPJR+Crx5MFFMFh1e3SeyzYt0N2AwFnkFUBlja7wqUUL6JPdQ==} + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + engines: {node: '>=20'} + + wxt@0.20.27: + resolution: {integrity: sha512-dm6yixz2awM4YMqpTJnsCa8aOPiTrjiIjbWslgR5hCjgwhuft+hLlla339Dt7gIKwQloee4oOruoK++vaX0APA==} + engines: {bun: '>=1.2.0', node: '>=20.12.0'} hasBin: true + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} - xml2js@0.5.0: - resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} xmlbuilder@11.0.1: @@ -2614,33 +2499,19 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - zip-dir@2.0.0: resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==} - zod@3.24.2: - resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} snapshots: @@ -2658,198 +2529,32 @@ snapshots: optionalDependencies: rollup: 4.40.0 - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@ant-design/colors@7.2.0': - dependencies: - '@ant-design/fast-color': 2.0.6 - - '@ant-design/colors@8.0.0': - dependencies: - '@ant-design/fast-color': 3.0.0 - - '@ant-design/cssinjs-utils@1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@babel/runtime': 7.27.0 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@ant-design/cssinjs@1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.27.0 - '@emotion/hash': 0.8.0 - '@emotion/unitless': 0.7.5 - classnames: 2.5.1 - csstype: 3.1.3 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - stylis: 4.3.6 - - '@ant-design/fast-color@2.0.6': - dependencies: - '@babel/runtime': 7.27.0 - - '@ant-design/fast-color@3.0.0': {} - - '@ant-design/icons-svg@4.4.2': {} - - '@ant-design/icons@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@ant-design/colors': 7.2.0 - '@ant-design/icons-svg': 4.4.2 - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@ant-design/icons@6.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@ant-design/colors': 8.0.0 - '@ant-design/icons-svg': 4.4.2 - '@rc-component/util': 1.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@ant-design/react-slick@1.1.2(react@18.3.1)': - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - json2mq: 0.2.0 - react: 18.3.1 - resize-observer-polyfill: 1.5.1 - throttle-debounce: 5.0.2 - '@babel/code-frame@7.26.2': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/core@7.26.10': + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.27.0 - '@babel/helper-compilation-targets': 7.27.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) - '@babel/helpers': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/template': 7.27.0 - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 - convert-source-map: 2.0.0 - debug: 4.4.0 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@babel/types': 7.29.7 - '@babel/generator@7.27.0': + '@babel/runtime@7.28.2': {} + + '@babel/types@7.29.7': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.27.0': - dependencies: - '@babel/compat-data': 7.26.8 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-module-imports@7.25.9': - dependencies: - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': - dependencies: - '@babel/core': 7.26.10 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.27.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.26.5': {} - - '@babel/helper-string-parser@7.25.9': {} - - '@babel/helper-validator-identifier@7.25.9': {} - - '@babel/helper-validator-option@7.25.9': {} - - '@babel/helpers@7.27.0': - dependencies: - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 - - '@babel/parser@7.27.0': - dependencies: - '@babel/types': 7.27.0 - - '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.10)': - dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.10)': - dependencies: - '@babel/core': 7.26.10 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/runtime@7.24.7': - dependencies: - regenerator-runtime: 0.14.1 - - '@babel/runtime@7.27.0': - dependencies: - regenerator-runtime: 0.14.1 - - '@babel/template@7.27.0': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 - - '@babel/traverse@7.27.0': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 - debug: 4.4.0 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.27.0': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@devicefarmer/adbkit-logcat@2.1.3': {} '@devicefarmer/adbkit-monkey@1.2.1': {} - '@devicefarmer/adbkit@3.2.6': + '@devicefarmer/adbkit@3.3.8': dependencies: '@devicefarmer/adbkit-logcat': 2.1.3 '@devicefarmer/adbkit-monkey': 1.2.1 @@ -2861,113 +2566,129 @@ snapshots: transitivePeerDependencies: - supports-color - '@emotion/hash@0.8.0': {} - - '@emotion/unitless@0.7.5': {} - - '@esbuild/aix-ppc64@0.25.2': - optional: true - - '@esbuild/android-arm64@0.25.2': - optional: true - - '@esbuild/android-arm@0.25.2': - optional: true - - '@esbuild/android-x64@0.25.2': - optional: true - - '@esbuild/darwin-arm64@0.25.2': - optional: true - - '@esbuild/darwin-x64@0.25.2': - optional: true - - '@esbuild/freebsd-arm64@0.25.2': - optional: true - - '@esbuild/freebsd-x64@0.25.2': - optional: true - - '@esbuild/linux-arm64@0.25.2': - optional: true - - '@esbuild/linux-arm@0.25.2': - optional: true - - '@esbuild/linux-ia32@0.25.2': - optional: true - - '@esbuild/linux-loong64@0.25.2': - optional: true - - '@esbuild/linux-mips64el@0.25.2': - optional: true - - '@esbuild/linux-ppc64@0.25.2': - optional: true - - '@esbuild/linux-riscv64@0.25.2': - optional: true - - '@esbuild/linux-s390x@0.25.2': - optional: true - - '@esbuild/linux-x64@0.25.2': - optional: true - - '@esbuild/netbsd-arm64@0.25.2': - optional: true - - '@esbuild/netbsd-x64@0.25.2': - optional: true - - '@esbuild/openbsd-arm64@0.25.2': - optional: true - - '@esbuild/openbsd-x64@0.25.2': - optional: true - - '@esbuild/sunos-x64@0.25.2': - optional: true - - '@esbuild/win32-arm64@0.25.2': - optional: true - - '@esbuild/win32-ia32@0.25.2': - optional: true - - '@esbuild/win32-x64@0.25.2': - optional: true - - '@jridgewell/gen-mapping@0.3.8': + '@emnapi/core@1.11.1': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true - '@jridgewell/resolve-uri@3.1.2': {} + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true - '@jridgewell/set-array@1.2.1': {} + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.8.0 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.12': {} '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec@1.5.5': {} - '@nodelib/fs.scandir@2.1.5': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + '@oxc-project/types@0.139.0': {} '@pnpm/config.env-replace@1.1.0': {} @@ -2981,80 +2702,304 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@rc-component/async-validator@5.0.4': - dependencies: - '@babel/runtime': 7.27.0 + '@radix-ui/primitive@1.1.5': {} - '@rc-component/color-picker@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@ant-design/fast-color': 2.0.6 - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@rc-component/context@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-collection@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@rc-component/mini-decimal@1.1.0': + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@rc-component/mutate-observer@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@rc-component/portal@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@rc-component/qrcode@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@rc-component/tour@1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@rc-component/trigger@2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-popper@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@rc-component/util@1.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-is: 18.3.1 + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-presence@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-switch@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-tabs@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-tooltip@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/rect@1.1.2': {} + + '@rolldown/binding-android-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-x64@1.1.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.5': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.5': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.5': + optional: true + + '@rolldown/pluginutils@1.0.1': {} '@rollup/rollup-android-arm-eabi@4.40.0': optional: true @@ -3116,32 +3061,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.0': optional: true - '@sindresorhus/is@5.6.0': {} + '@standard-schema/spec@1.1.0': {} - '@szmarczak/http-timer@5.0.1': + '@tybys/wasm-util@0.10.3': dependencies: - defer-to-connect: 2.0.1 + tslib: 2.8.1 + optional: true - '@types/babel__core@7.20.5': + '@types/chai@5.2.3': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.7 + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.27.0 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 - - '@types/babel__traverse@7.20.7': - dependencies: - '@babel/types': 7.27.0 + '@types/deep-eql@4.0.2': {} '@types/estree@1.0.7': {} @@ -3153,72 +3085,163 @@ snapshots: '@types/har-format@1.2.16': {} - '@types/http-cache-semantics@4.0.4': {} - '@types/minimatch@3.0.5': {} '@types/node@22.14.1': dependencies: undici-types: 6.21.0 - '@types/prop-types@15.7.14': {} - - '@types/react-dom@18.3.6(@types/react@18.3.20)': + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - '@types/react': 18.3.20 + '@types/react': 19.2.17 - '@types/react@18.3.20': + '@types/react@19.2.17': dependencies: - '@types/prop-types': 15.7.14 - csstype: 3.1.3 + csstype: 3.2.3 - '@types/uuid@10.0.0': {} + '@types/webextension-polyfill@0.12.5': {} - '@types/yauzl@2.10.3': + '@types/ws@8.18.1': dependencies: '@types/node': 22.14.1 + + '@typescript/typescript-aix-ppc64@7.0.2': optional: true - '@vitejs/plugin-react@4.3.4(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2))': - dependencies: - '@babel/core': 7.26.10 - '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) - '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) - '@types/babel__core': 7.20.5 - react-refresh: 0.14.2 - vite: 6.2.6(@types/node@22.14.1)(jiti@2.4.2) - transitivePeerDependencies: - - supports-color + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true - '@webext-core/fake-browser@1.3.2': + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@vitejs/plugin-react@6.0.3(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0))': dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) + + '@vitest/expect@4.1.10': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.10(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0))': + dependencies: + '@vitest/spy': 4.1.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) + + '@vitest/pretty-format@4.1.10': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.10': + dependencies: + '@vitest/utils': 4.1.10 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.10': {} + + '@vitest/utils@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + '@webext-core/fake-browser@1.5.2': + dependencies: + '@types/webextension-polyfill': 0.12.5 lodash.merge: 4.6.2 - '@webext-core/isolated-element@1.1.2': + '@webext-core/isolated-element@1.1.5': dependencies: is-potential-custom-element-name: 1.0.1 '@webext-core/match-patterns@1.0.3': {} - '@wxt-dev/browser@0.0.310': + '@wxt-dev/browser@0.2.2': dependencies: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.16 - '@wxt-dev/module-react@1.1.3(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2))(wxt@0.20.0(@types/node@22.14.1)(jiti@2.4.2)(rollup@4.40.0))': + '@wxt-dev/module-react@1.2.2(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0))(wxt@0.20.27(@types/node@22.14.1)(jiti@2.7.0)(rollup@4.40.0))': dependencies: - '@vitejs/plugin-react': 4.3.4(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)) - wxt: 0.20.0(@types/node@22.14.1)(jiti@2.4.2)(rollup@4.40.0) + '@vitejs/plugin-react': 6.0.3(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0)) + vite: 8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) + wxt: 0.20.27(@types/node@22.14.1)(jiti@2.7.0)(rollup@4.40.0) transitivePeerDependencies: - - supports-color - - vite + - '@rolldown/plugin-babel' + - babel-plugin-react-compiler '@wxt-dev/storage@1.1.1': dependencies: async-mutex: 0.5.0 dequal: 2.0.3 - acorn@8.14.1: {} + acorn@8.17.0: {} adm-zip@0.5.16: {} @@ -3234,206 +3257,97 @@ snapshots: ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@6.2.1: {} - antd@5.24.6(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@ant-design/colors': 7.2.0 - '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ant-design/cssinjs-utils': 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ant-design/fast-color': 2.0.6 - '@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ant-design/react-slick': 1.1.2(react@18.3.1) - '@babel/runtime': 7.27.0 - '@rc-component/color-picker': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/mutate-observer': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/qrcode': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/tour': 1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - copy-to-clipboard: 3.3.3 - dayjs: 1.11.13 - rc-cascader: 3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-checkbox: 3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-collapse: 3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-drawer: 7.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-field-form: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-image: 7.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-input: 1.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-input-number: 9.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-mentions: 2.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-notification: 5.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-pagination: 5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-picker: 4.11.3(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-rate: 2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-segmented: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-select: 14.16.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-slider: 11.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-steps: 6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-switch: 4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-table: 7.50.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tabs: 15.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-textarea: 1.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tooltip: 6.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tree-select: 5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-upload: 4.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - scroll-into-view-if-needed: 3.1.0 - throttle-debounce: 5.0.2 - transitivePeerDependencies: - - date-fns - - luxon - - moment - - any-promise@1.3.0: {} + ansi-styles@6.2.3: {} array-differ@4.0.0: {} array-union@3.0.1: {} + assertion-error@2.0.1: {} + async-mutex@0.5.0: dependencies: tslib: 2.8.1 async@3.2.6: {} - at-least-node@1.0.0: {} + atomic-sleep@1.0.0: {} + + atomically@2.1.1: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 balanced-match@1.0.2: {} - base64-js@1.5.1: {} - - big-integer@1.6.52: {} - - bl@5.1.0: - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.2 - bluebird@3.7.2: {} - boolbase@1.0.0: {} + boolbase@2.0.0: {} - boxen@7.1.1: + boxen@8.0.1: dependencies: ansi-align: 3.0.1 - camelcase: 7.0.1 + camelcase: 8.0.0 chalk: 5.4.1 cli-boxes: 3.0.0 - string-width: 5.1.2 - type-fest: 2.19.0 - widest-line: 4.0.1 - wrap-ansi: 8.1.0 - - bplist-parser@0.2.0: - dependencies: - big-integer: 1.6.52 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.24.4: - dependencies: - caniuse-lite: 1.0.30001713 - electron-to-chromium: 1.5.136 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.4) - - buffer-crc32@0.2.13: {} + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bundle-name@3.0.0: - dependencies: - run-applescript: 5.0.0 - bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 - bunyan@1.8.15: - optionalDependencies: - dtrace-provider: 0.8.8 - moment: 2.30.1 - mv: 2.1.1 - safe-json-stringify: 1.2.0 - - c12@3.0.3(magicast@0.3.5): + c12@3.3.4(magicast@0.5.3): dependencies: - chokidar: 4.0.3 - confbox: 0.2.2 - defu: 6.1.4 - dotenv: 16.5.0 - exsolve: 1.0.4 - giget: 2.0.0 - jiti: 2.4.2 + chokidar: 5.0.0 + confbox: 0.2.4 + defu: 6.1.7 + dotenv: 17.4.2 + exsolve: 1.1.0 + giget: 3.3.0 + jiti: 2.7.0 ohash: 2.0.11 pathe: 2.0.3 - perfect-debounce: 1.0.0 - pkg-types: 2.1.0 - rc9: 2.1.2 + perfect-debounce: 2.1.0 + pkg-types: 2.3.1 + rc9: 3.0.1 optionalDependencies: - magicast: 0.3.5 + magicast: 0.5.3 cac@6.7.14: {} - cacheable-lookup@7.0.0: {} + cac@7.0.0: {} - cacheable-request@10.2.14: - dependencies: - '@types/http-cache-semantics': 4.0.4 - get-stream: 6.0.1 - http-cache-semantics: 4.1.1 - keyv: 4.5.4 - mimic-response: 4.0.0 - normalize-url: 8.0.1 - responselike: 3.0.0 + camelcase@8.0.0: {} - camelcase@7.0.1: {} - - caniuse-lite@1.0.30001713: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 + chai@6.2.2: {} chalk@5.4.1: {} - chokidar@4.0.3: + chokidar@5.0.0: dependencies: - readdirp: 4.1.2 + readdirp: 5.0.0 - chrome-launcher@1.1.0: + chrome-launcher@1.2.0: dependencies: '@types/node': 22.14.1 escape-string-regexp: 4.0.0 @@ -3442,47 +3356,28 @@ snapshots: transitivePeerDependencies: - supports-color - ci-info@3.9.0: {} - - ci-info@4.2.0: {} + ci-info@4.4.0: {} citty@0.1.6: dependencies: consola: 3.4.2 - classnames@2.5.1: {} + citty@0.2.2: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 cli-boxes@3.0.0: {} - cli-cursor@4.0.0: - dependencies: - restore-cursor: 4.0.0 - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 - cli-highlight@2.1.11: + cli-truncate@5.2.0: dependencies: - chalk: 4.1.2 - highlight.js: 10.7.3 - mz: 2.7.0 - parse5: 5.1.1 - parse5-htmlparser2-tree-adapter: 6.0.1 - yargs: 16.2.0 - - cli-spinners@2.9.2: {} - - cli-truncate@4.0.0: - dependencies: - slice-ansi: 5.0.0 - string-width: 7.2.0 - - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + slice-ansi: 8.0.0 + string-width: 8.2.2 cliui@8.0.1: dependencies: @@ -3490,7 +3385,7 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - clone@1.0.4: {} + clsx@2.1.1: {} color-convert@2.0.1: dependencies: @@ -3498,16 +3393,12 @@ snapshots: color-name@1.1.4: {} - colorette@2.0.20: {} - commander@2.9.0: dependencies: graceful-readlink: 1.0.1 commander@9.5.0: {} - compute-scroll-into-view@3.1.1: {} - concat-map@0.0.1: {} concat-stream@1.6.2: @@ -3521,54 +3412,39 @@ snapshots: confbox@0.2.2: {} + confbox@0.2.4: {} + config-chain@1.1.13: dependencies: ini: 1.3.8 proto-list: 1.2.4 - configstore@6.0.0: + configstore@7.1.0: dependencies: - dot-prop: 6.0.1 + atomically: 2.1.1 + dot-prop: 9.0.0 graceful-fs: 4.2.11 - unique-string: 3.0.0 - write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 consola@3.4.2: {} convert-source-map@2.0.0: {} - copy-to-clipboard@3.3.3: - dependencies: - toggle-selection: 1.0.6 - core-util-is@1.0.3: {} - cross-spawn@7.0.6: + css-select@7.0.0: dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 + boolbase: 2.0.0 + css-what: 8.0.0 + domhandler: 6.0.1 + domutils: 4.0.2 + nth-check: 3.0.1 - crypto-random-string@4.0.0: - dependencies: - type-fest: 1.4.0 - - css-select@5.1.0: - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 5.0.3 - domutils: 3.2.2 - nth-check: 2.1.1 - - css-what@6.1.0: {} + css-what@8.0.0: {} cssom@0.5.0: {} - csstype@3.1.3: {} - - dayjs@1.11.13: {} + csstype@3.2.3: {} debounce@1.2.1: {} @@ -3580,101 +3456,90 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.0: - dependencies: - ms: 2.1.3 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - deep-extend@0.6.0: {} - default-browser-id@3.0.0: - dependencies: - bplist-parser: 0.2.0 - untildify: 4.0.0 - default-browser-id@5.0.0: {} - default-browser@4.0.0: - dependencies: - bundle-name: 3.0.0 - default-browser-id: 3.0.0 - execa: 7.2.0 - titleize: 3.0.0 - - default-browser@5.2.1: + default-browser@5.5.0: dependencies: bundle-name: 4.1.0 default-browser-id: 5.0.0 - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - defer-to-connect@2.0.1: {} - define-lazy-prop@2.0.0: {} define-lazy-prop@3.0.0: {} defu@6.1.4: {} + defu@6.1.7: {} + dequal@2.0.3: {} destr@2.0.5: {} + detect-libc@2.1.2: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 + dom-serializer@3.1.1: + dependencies: + domelementtype: 3.0.0 + domhandler: 6.0.1 + entities: 8.0.0 + domelementtype@2.3.0: {} + domelementtype@3.0.0: {} + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 + domhandler@6.0.1: + dependencies: + domelementtype: 3.0.0 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 domhandler: 5.0.3 - dot-prop@6.0.1: + domutils@4.0.2: dependencies: - is-obj: 2.0.0 + dom-serializer: 3.1.1 + domelementtype: 3.0.0 + domhandler: 6.0.1 - dotenv-expand@12.0.1: + dot-prop@9.0.0: + dependencies: + type-fest: 4.41.0 + + dotenv-expand@12.0.3: dependencies: dotenv: 16.5.0 dotenv@16.5.0: {} - dtrace-provider@0.8.8: + dotenv@17.4.2: {} + + ecdsa-sig-formatter@1.0.11: dependencies: - nan: 2.22.2 - optional: true - - eastasianwidth@0.2.0: {} - - electron-to-chromium@1.5.136: {} + safe-buffer: 5.2.1 emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - entities@4.5.0: {} - entities@6.0.0: {} + entities@7.0.1: {} + + entities@8.0.0: {} environment@1.1.0: {} @@ -3682,37 +3547,38 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-module-lexer@1.6.0: {} + es-module-lexer@2.3.1: {} es6-error@4.1.1: {} - esbuild@0.25.2: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.2 - '@esbuild/android-arm': 0.25.2 - '@esbuild/android-arm64': 0.25.2 - '@esbuild/android-x64': 0.25.2 - '@esbuild/darwin-arm64': 0.25.2 - '@esbuild/darwin-x64': 0.25.2 - '@esbuild/freebsd-arm64': 0.25.2 - '@esbuild/freebsd-x64': 0.25.2 - '@esbuild/linux-arm': 0.25.2 - '@esbuild/linux-arm64': 0.25.2 - '@esbuild/linux-ia32': 0.25.2 - '@esbuild/linux-loong64': 0.25.2 - '@esbuild/linux-mips64el': 0.25.2 - '@esbuild/linux-ppc64': 0.25.2 - '@esbuild/linux-riscv64': 0.25.2 - '@esbuild/linux-s390x': 0.25.2 - '@esbuild/linux-x64': 0.25.2 - '@esbuild/netbsd-arm64': 0.25.2 - '@esbuild/netbsd-x64': 0.25.2 - '@esbuild/openbsd-arm64': 0.25.2 - '@esbuild/openbsd-x64': 0.25.2 - '@esbuild/sunos-x64': 0.25.2 - '@esbuild/win32-arm64': 0.25.2 - '@esbuild/win32-ia32': 0.25.2 - '@esbuild/win32-x64': 0.25.2 + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 escalade@3.2.0: {} @@ -3726,113 +3592,40 @@ snapshots: dependencies: '@types/estree': 1.0.7 - eventemitter3@5.0.1: {} + eventemitter3@5.0.4: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - execa@7.2.0: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 + expect-type@1.4.0: {} exsolve@1.0.4: {} - extract-zip@2.0.1: - dependencies: - debug: 4.4.0 - get-stream: 5.2.0 - yauzl: 2.10.0 + exsolve@1.1.0: {} + + fast-redact@3.5.0: {} + + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color + picomatch: 4.0.5 - 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 + filesize@11.0.22: {} - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - - fdir@6.4.3(picomatch@4.0.2): - optionalDependencies: - picomatch: 4.0.2 - - filesize@10.1.6: {} - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - firefox-profile@4.6.0: + firefox-profile@4.7.0: dependencies: adm-zip: 0.5.16 - fs-extra: 9.0.1 - ini: 2.0.0 + fs-extra: 11.3.0 + ini: 4.1.3 minimist: 1.2.8 - xml2js: 0.5.0 + xml2js: 0.6.2 - form-data-encoder@2.1.4: {} + form-data-encoder@4.1.0: {} formdata-node@6.0.3: {} - fs-extra@11.2.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - fs-extra@9.0.1: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 1.0.0 - fsevents@2.3.3: optional: true @@ -3845,21 +3638,13 @@ snapshots: which: 1.2.4 winreg: 0.0.12 - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} get-east-asian-width@1.3.0: {} - get-port-please@3.1.2: {} + get-east-asian-width@1.6.0: {} - get-stream@5.2.0: - dependencies: - pump: 3.0.2 - - get-stream@6.0.1: {} - - get-stream@8.0.1: {} + get-port-please@3.2.0: {} giget@2.0.0: dependencies: @@ -3867,43 +3652,16 @@ snapshots: consola: 3.4.2 defu: 6.1.4 node-fetch-native: 1.6.6 - nypm: 0.6.0 + nypm: 0.6.8 pathe: 2.0.3 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 + giget@3.3.0: {} glob-to-regexp@0.4.1: {} - glob@6.0.4: + global-directory@4.0.1: dependencies: - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - optional: true - - global-dirs@3.0.1: - dependencies: - ini: 2.0.0 - - globals@11.12.0: {} - - got@12.6.1: - dependencies: - '@sindresorhus/is': 5.6.0 - '@szmarczak/http-timer': 5.0.1 - cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 - decompress-response: 6.0.0 - form-data-encoder: 2.1.4 - get-stream: 6.0.1 - http2-wrapper: 2.2.1 - lowercase-keys: 3.0.0 - p-cancelable: 3.0.0 - responselike: 3.0.0 + ini: 4.1.1 graceful-fs@4.2.10: {} @@ -3913,57 +3671,28 @@ snapshots: growly@1.3.0: {} - has-flag@4.0.0: {} - - has-yarn@3.0.0: {} - - highlight.js@10.7.3: {} - - hookable@5.5.3: {} + hookable@6.1.1: {} html-escaper@3.0.3: {} - htmlparser2@10.0.0: + htmlparser2@10.1.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - entities: 6.0.0 - - http-cache-semantics@4.1.1: {} - - http2-wrapper@2.2.1: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - human-signals@2.1.0: {} - - human-signals@4.3.1: {} - - human-signals@5.0.0: {} - - ieee754@1.2.1: {} + entities: 7.0.1 immediate@3.0.6: {} - import-lazy@4.0.0: {} - - import-meta-resolve@4.1.0: {} - - imurmurhash@0.1.4: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - optional: true + import-meta-resolve@4.2.0: {} inherits@2.0.4: {} ini@1.3.8: {} - ini@2.0.0: {} + ini@4.1.1: {} + + ini@4.1.3: {} is-absolute@0.1.7: dependencies: @@ -3971,46 +3700,36 @@ snapshots: is-arrayish@0.2.1: {} - is-ci@3.0.1: - dependencies: - ci-info: 3.9.0 - is-docker@2.2.1: {} is-docker@3.0.0: {} - is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-fullwidth-code-point@4.0.0: {} - is-fullwidth-code-point@5.0.0: dependencies: get-east-asian-width: 1.3.0 - is-glob@4.0.3: + is-fullwidth-code-point@5.1.0: dependencies: - is-extglob: 2.1.1 + get-east-asian-width: 1.6.0 + + is-in-ci@1.0.0: {} + + is-in-ssh@1.0.0: {} is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 - is-installed-globally@0.4.0: + is-installed-globally@1.0.0: dependencies: - global-dirs: 3.0.1 - is-path-inside: 3.0.3 - - is-interactive@2.0.0: {} + global-directory: 4.0.1 + is-path-inside: 4.0.0 is-npm@6.0.0: {} - is-number@7.0.0: {} - - is-obj@2.0.0: {} - - is-path-inside@3.0.3: {} + is-path-inside@4.0.0: {} is-plain-object@2.0.4: dependencies: @@ -4022,26 +3741,14 @@ snapshots: is-relative@0.1.3: {} - is-stream@2.0.1: {} - - is-stream@3.0.0: {} - - is-typedarray@1.0.0: {} - - is-unicode-supported@1.3.0: {} - - is-unicode-supported@2.1.0: {} - is-wsl@2.2.0: dependencies: is-docker: 2.2.1 - is-wsl@3.1.0: + is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 - is-yarn-global@0.4.1: {} - isarray@1.0.0: {} isexe@1.1.2: {} @@ -4050,22 +3757,14 @@ snapshots: isobject@3.0.1: {} - jiti@2.4.2: {} + jiti@2.7.0: {} js-tokens@4.0.0: {} js-tokens@9.0.1: {} - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - json-parse-even-better-errors@3.0.2: {} - json2mq@0.2.0: - dependencies: - string-convert: 0.2.1 - json5@2.2.3: {} jsonfile@6.1.0: @@ -4074,6 +3773,19 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.1 + jszip@3.10.1: dependencies: lie: 3.3.0 @@ -4081,15 +3793,24 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - keyv@4.5.4: + jwa@2.0.1: dependencies: - json-buffer: 3.0.1 + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 kleur@3.0.3: {} - latest-version@7.0.0: + ky@1.14.3: {} + + latest-version@9.0.0: dependencies: - package-json: 8.1.1 + package-json: 10.0.1 lie@3.3.0: dependencies: @@ -4102,24 +3823,72 @@ snapshots: transitivePeerDependencies: - supports-color + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@2.0.4: {} - linkedom@0.18.9: + linkedom@0.18.13: dependencies: - css-select: 5.1.0 + css-select: 7.0.0 cssom: 0.5.0 html-escaper: 3.0.3 - htmlparser2: 10.0.0 + htmlparser2: 10.1.0 uhyphen: 0.2.0 - listr2@8.3.2: + listr2@10.2.2: dependencies: - cli-truncate: 4.0.0 - colorette: 2.0.20 - eventemitter3: 5.0.1 + cli-truncate: 5.2.0 + eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.0 + wrap-ansi: 10.0.0 local-pkg@1.1.1: dependencies: @@ -4127,23 +3896,21 @@ snapshots: pkg-types: 2.1.0 quansync: 0.2.10 - lodash.camelcase@4.3.0: {} + lodash.includes@4.3.0: {} - lodash.kebabcase@4.1.1: {} + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} lodash.merge@4.6.2: {} - lodash.snakecase@4.1.1: {} - - log-symbols@5.1.0: - dependencies: - chalk: 5.4.1 - is-unicode-supported: 1.3.0 - - log-symbols@6.0.0: - dependencies: - chalk: 5.4.1 - is-unicode-supported: 1.3.0 + lodash.once@4.1.1: {} log-update@6.1.0: dependencies: @@ -4153,24 +3920,22 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - loose-envify@1.4.0: + lucide-react@1.24.0(react@19.2.7): dependencies: - js-tokens: 4.0.0 - - lowercase-keys@3.0.0: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 + react: 19.2.7 magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - magicast@0.3.5: + magic-string@0.30.21: dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 source-map-js: 1.2.1 make-error@1.3.6: {} @@ -4179,52 +3944,21 @@ snapshots: marky@1.3.0: {} - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mimic-fn@2.1.0: {} - - mimic-fn@4.0.0: {} - mimic-function@5.0.1: {} - mimic-response@3.1.0: {} - - mimic-response@4.0.0: {} - - minimatch@10.0.1: - dependencies: - brace-expansion: 2.0.1 - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 minimist@1.2.8: {} - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - optional: true - - mkdirp@3.0.1: {} - mlly@1.7.4: dependencies: - acorn: 8.14.1 + acorn: 8.17.0 pathe: 2.0.3 pkg-types: 1.3.1 ufo: 1.6.1 - moment@2.30.1: - optional: true - ms@2.0.0: {} ms@2.1.3: {} @@ -4236,31 +3970,18 @@ snapshots: array-union: 3.0.1 minimatch: 3.1.2 - mv@2.1.1: + nano-spawn@2.1.0: {} + + nanoid@3.3.16: {} + + nanospinner@1.2.2: dependencies: - mkdirp: 0.5.6 - ncp: 2.0.0 - rimraf: 2.4.5 - optional: true - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nan@2.22.2: - optional: true - - nano-spawn@0.2.0: {} - - nanoid@3.3.11: {} - - ncp@2.0.0: - optional: true + picocolors: 1.1.1 node-fetch-native@1.6.6: {} + node-fetch-native@1.6.7: {} + node-forge@1.3.1: {} node-notifier@10.0.1: @@ -4272,75 +3993,42 @@ snapshots: uuid: 8.3.2 which: 2.0.2 - node-releases@2.0.19: {} - normalize-path@3.0.0: {} - normalize-url@8.0.1: {} - - npm-run-path@4.0.1: + nth-check@3.0.1: dependencies: - path-key: 3.1.1 + boolbase: 2.0.0 - npm-run-path@5.3.0: + nypm@0.6.8: dependencies: - path-key: 4.0.0 - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - nypm@0.3.12: - dependencies: - citty: 0.1.6 - consola: 3.4.2 - execa: 8.0.1 - pathe: 1.1.2 - pkg-types: 1.3.1 - ufo: 1.6.1 - - nypm@0.6.0: - dependencies: - citty: 0.1.6 - consola: 3.4.2 + citty: 0.2.2 pathe: 2.0.3 - pkg-types: 2.1.0 - tinyexec: 0.3.2 + tinyexec: 1.2.4 - object-assign@4.1.1: {} + obug@2.1.3: {} - ofetch@1.4.1: + ofetch@1.5.1: dependencies: destr: 2.0.5 - node-fetch-native: 1.6.6 + node-fetch-native: 1.6.7 ufo: 1.6.1 - ohash@1.1.6: {} - ohash@2.0.11: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 + on-exit-leak-free@2.1.2: {} onetime@7.0.0: dependencies: mimic-function: 5.0.1 - open@10.1.0: + open@11.0.0: dependencies: - default-browser: 5.2.1 + default-browser: 5.5.0 define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 is-inside-container: 1.0.0 - is-wsl: 3.1.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.1 open@8.4.2: dependencies: @@ -4348,44 +4036,11 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - open@9.1.0: - dependencies: - default-browser: 4.0.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 2.2.0 - - ora@6.3.1: - dependencies: - chalk: 5.4.1 - cli-cursor: 4.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - stdin-discarder: 0.1.0 - strip-ansi: 7.1.0 - wcwidth: 1.0.1 - - ora@8.2.0: - dependencies: - chalk: 5.4.1 - cli-cursor: 5.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.1.0 - os-shim@0.1.3: {} - p-cancelable@3.0.0: {} - - package-json@8.1.1: + package-json@10.0.1: dependencies: - got: 12.6.1 + ky: 1.14.3 registry-auth-token: 5.1.0 registry-url: 6.0.1 semver: 7.7.1 @@ -4400,34 +4055,35 @@ snapshots: lines-and-columns: 2.0.4 type-fest: 3.13.1 - parse5-htmlparser2-tree-adapter@6.0.1: - dependencies: - parse5: 6.0.1 - - parse5@5.1.1: {} - - parse5@6.0.1: {} - - path-is-absolute@1.0.1: - optional: true - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - pathe@1.1.2: {} - pathe@2.0.3: {} - pend@1.2.0: {} - - perfect-debounce@1.0.0: {} + perfect-debounce@2.1.0: {} picocolors@1.1.1: {} picomatch@2.3.1: {} - picomatch@4.0.2: {} + picomatch@4.0.5: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@9.7.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 3.2.0 pkg-types@1.3.1: dependencies: @@ -4441,14 +4097,26 @@ snapshots: exsolve: 1.0.4 pathe: 2.0.3 - postcss@8.5.3: + pkg-types@2.3.1: dependencies: - nanoid: 3.3.11 + confbox: 0.2.4 + exsolve: 1.1.0 + pathe: 2.0.3 + + playwright-core@1.61.1: {} + + postcss@8.5.19: + dependencies: + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 + powershell-utils@0.1.0: {} + process-nextick-args@2.0.1: {} + process-warning@5.0.0: {} + promise-toolbox@0.21.0: dependencies: make-error: 1.3.6 @@ -4460,30 +4128,17 @@ snapshots: proto-list@1.2.4: {} - publish-browser-extension@3.0.0: + publish-browser-extension@4.0.5: dependencies: cac: 6.7.14 - cli-highlight: 2.1.11 consola: 3.4.2 - dotenv: 16.5.0 - extract-zip: 2.0.1 + dotenv: 17.4.2 + form-data-encoder: 4.1.0 formdata-node: 6.0.3 - listr2: 8.3.2 - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.snakecase: 4.1.1 - ofetch: 1.4.1 - open: 9.1.0 - ora: 6.3.1 - prompts: 2.4.2 - zod: 3.24.2 - transitivePeerDependencies: - - supports-color - - pump@3.0.2: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 + jsonwebtoken: 9.0.3 + listr2: 10.2.2 + ofetch: 1.5.1 + zod: 4.4.3 pupa@3.1.0: dependencies: @@ -4491,333 +4146,11 @@ snapshots: quansync@0.2.10: {} - queue-microtask@1.2.3: {} + quick-format-unescaped@4.0.4: {} - quick-lru@5.1.1: {} - - rc-cascader@3.33.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + rc9@3.0.1: dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-select: 14.16.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-checkbox@3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-collapse@3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-dialog@9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-drawer@7.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-dropdown@4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/async-validator': 5.0.4 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-image@7.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-input-number@9.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/mini-decimal': 1.1.0 - classnames: 2.5.1 - rc-input: 1.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-input@1.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-mentions@2.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-input: 1.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-textarea: 1.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-menu@9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-motion@2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-notification@5.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-overflow@1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-pagination@5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-picker@4.11.3(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - dayjs: 1.11.13 - moment: 2.30.1 - - rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-rate@2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-resize-observer@1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - resize-observer-polyfill: 1.5.1 - - rc-segmented@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-select@14.16.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-virtual-list: 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-slider@11.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-steps@6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-switch@4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-table@7.50.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/context': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-virtual-list: 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-tabs@15.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-textarea@1.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-input: 1.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-tooltip@6.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-tree-select@5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-select: 14.16.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-tree@5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-virtual-list: 3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-upload@4.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc-util@5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-is: 18.3.1 - - rc-virtual-list@3.18.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.27.0 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - rc9@2.1.2: - dependencies: - defu: 6.1.4 + defu: 6.1.7 destr: 2.0.5 rc@1.2.8: @@ -4827,19 +4160,12 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dom@18.3.1(react@18.3.1): + react-dom@19.2.7(react@19.2.7): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.2.7 + scheduler: 0.27.0 - react-is@18.3.1: {} - - react-refresh@0.14.2: {} - - react@18.3.1: - dependencies: - loose-envify: 1.4.0 + react@19.2.7: {} readable-stream@2.3.8: dependencies: @@ -4851,15 +4177,9 @@ snapshots: string_decoder: 1.1.1 util-deprecate: 1.0.2 - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 + readdirp@5.0.0: {} - readdirp@4.1.2: {} - - regenerator-runtime@0.14.1: {} + real-require@0.2.0: {} registry-auth-token@5.1.0: dependencies: @@ -4871,32 +4191,33 @@ snapshots: require-directory@2.1.1: {} - resize-observer-polyfill@1.5.1: {} - - resolve-alpn@1.2.1: {} - - responselike@3.0.0: - dependencies: - lowercase-keys: 3.0.0 - - restore-cursor@4.0.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - restore-cursor@5.1.0: dependencies: onetime: 7.0.0 signal-exit: 4.1.0 - reusify@1.1.0: {} - rfdc@1.4.1: {} - rimraf@2.4.5: + rolldown@1.1.5: dependencies: - glob: 6.0.4 - optional: true + '@oxc-project/types': 0.139.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 rollup@4.40.0: dependencies: @@ -4923,42 +4244,22 @@ snapshots: '@rollup/rollup-win32-ia32-msvc': 4.40.0 '@rollup/rollup-win32-x64-msvc': 4.40.0 fsevents: 2.3.3 - - run-applescript@5.0.0: - dependencies: - execa: 5.1.1 + optional: true run-applescript@7.0.0: {} - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} - safe-json-stringify@1.2.0: - optional: true + safe-stable-stringify@2.5.0: {} sax@1.4.1: {} - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 - - scroll-into-view-if-needed@3.1.0: - dependencies: - compute-scroll-into-view: 3.1.1 + scheduler@0.27.0: {} scule@1.3.0: {} - semver-diff@4.0.0: - dependencies: - semver: 7.7.1 - - semver@6.3.1: {} - semver@7.7.1: {} set-value@4.1.0: @@ -4968,32 +4269,30 @@ snapshots: setimmediate@1.0.5: {} - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - shell-quote@1.7.3: {} shellwords@0.1.1: {} - signal-exit@3.0.7: {} + siginfo@2.0.0: {} signal-exit@4.1.0: {} sisteransi@1.0.5: {} - slice-ansi@5.0.0: - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 - slice-ansi@7.1.0: dependencies: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -5010,17 +4309,15 @@ snapshots: concat-stream: 1.6.2 os-shim: 0.1.3 + split2@4.2.0: {} + split@1.0.1: dependencies: through: 2.3.8 - stdin-discarder@0.1.0: - dependencies: - bl: 5.1.0 + stackback@0.0.2: {} - stdin-discarder@0.2.2: {} - - string-convert@0.2.1: {} + std-env@4.2.0: {} string-width@4.2.3: dependencies: @@ -5028,26 +4325,21 @@ snapshots: 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.0 - string-width@7.2.0: dependencies: emoji-regex: 10.4.0 - get-east-asian-width: 1.3.0 - strip-ansi: 7.1.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -5056,70 +4348,75 @@ snapshots: dependencies: ansi-regex: 6.1.0 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-bom@5.0.0: {} - strip-final-newline@2.0.0: {} - - strip-final-newline@3.0.0: {} - strip-json-comments@2.0.1: {} - strip-json-comments@5.0.1: {} + strip-json-comments@5.0.2: {} strip-literal@3.0.0: dependencies: js-tokens: 9.0.1 - stylis@4.3.6: {} - - supports-color@7.2.0: + stubborn-fs@2.0.0: dependencies: - has-flag: 4.0.0 + stubborn-utils: 1.0.2 - thenify-all@1.6.0: + stubborn-utils@1.0.2: {} + + thread-stream@3.2.0: dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - throttle-debounce@5.0.2: {} + real-require: 0.2.0 through@2.3.8: {} - tinyexec@0.3.2: {} + tinybench@2.9.0: {} - tinyglobby@0.2.12: + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: dependencies: - fdir: 6.4.3(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 - titleize@3.0.0: {} + tinyrainbow@3.1.0: {} - tmp@0.2.3: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toggle-selection@1.0.6: {} + tmp@0.2.5: {} tslib@2.8.1: {} - type-fest@1.4.0: {} - - type-fest@2.19.0: {} - type-fest@3.13.1: {} - typedarray-to-buffer@3.1.5: - dependencies: - is-typedarray: 1.0.0 + type-fest@4.41.0: {} typedarray@0.0.6: {} - typescript@5.8.3: {} + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 ufo@1.6.1: {} @@ -5129,144 +4426,152 @@ snapshots: unimport@4.2.0: dependencies: - acorn: 8.14.1 + acorn: 8.17.0 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 local-pkg: 1.1.1 magic-string: 0.30.17 mlly: 1.7.4 pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.5 pkg-types: 2.1.0 scule: 1.3.0 strip-literal: 3.0.0 - tinyglobby: 0.2.12 + tinyglobby: 0.2.17 unplugin: 2.3.2 unplugin-utils: 0.2.4 - unique-string@3.0.0: - dependencies: - crypto-random-string: 4.0.0 - - universalify@1.0.0: {} - universalify@2.0.1: {} unplugin-utils@0.2.4: dependencies: pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.5 unplugin@2.3.2: dependencies: - acorn: 8.14.1 - picomatch: 4.0.2 + acorn: 8.17.0 + picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 - untildify@4.0.0: {} - - update-browserslist-db@1.1.3(browserslist@4.24.4): + update-notifier@7.3.1: dependencies: - browserslist: 4.24.4 - escalade: 3.2.0 - picocolors: 1.1.1 - - update-notifier@6.0.2: - dependencies: - boxen: 7.1.1 + boxen: 8.0.1 chalk: 5.4.1 - configstore: 6.0.0 - has-yarn: 3.0.0 - import-lazy: 4.0.0 - is-ci: 3.0.1 - is-installed-globally: 0.4.0 + configstore: 7.1.0 + is-in-ci: 1.0.0 + is-installed-globally: 1.0.0 is-npm: 6.0.0 - is-yarn-global: 0.4.1 - latest-version: 7.0.0 + latest-version: 9.0.0 pupa: 3.1.0 semver: 7.7.1 - semver-diff: 4.0.0 xdg-basedir: 5.1.0 util-deprecate@1.0.2: {} - uuid@11.1.0: {} + uuid@14.0.1: {} uuid@8.3.2: {} - vite-node@3.1.1(@types/node@22.14.1)(jiti@2.4.2): + valibot@1.4.2(typescript@7.0.2): + optionalDependencies: + typescript: 7.0.2 + + vite-node@6.0.0(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0): dependencies: - cac: 6.7.14 - debug: 4.4.0 - es-module-lexer: 1.6.0 + cac: 7.0.0 + es-module-lexer: 2.3.1 + obug: 2.1.3 pathe: 2.0.3 - vite: 6.2.6(@types/node@22.14.1)(jiti@2.4.2) + vite: 8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) transitivePeerDependencies: - '@types/node' + - '@vitejs/devtools' + - esbuild - jiti - less - - lightningcss - sass - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - yaml - vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2): + vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0): dependencies: - esbuild: 0.25.2 - postcss: 8.5.3 - rollup: 4.40.0 + lightningcss: 1.32.0 + picomatch: 4.0.5 + postcss: 8.5.19 + rolldown: 1.1.5 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 22.14.1 + esbuild: 0.27.7 fsevents: 2.3.3 - jiti: 2.4.2 + jiti: 2.7.0 - watchpack@2.4.1: + vitest@4.1.10(@types/node@22.14.1)(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.14.1 + transitivePeerDependencies: + - msw + + watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - wcwidth@1.0.1: + web-ext-run@0.2.4: dependencies: - defaults: 1.0.4 - - web-ext-run@0.2.2: - dependencies: - '@babel/runtime': 7.24.7 - '@devicefarmer/adbkit': 3.2.6 - bunyan: 1.8.15 - chrome-launcher: 1.1.0 + '@babel/runtime': 7.28.2 + '@devicefarmer/adbkit': 3.3.8 + chrome-launcher: 1.2.0 debounce: 1.2.1 es6-error: 4.1.1 - firefox-profile: 4.6.0 - fs-extra: 11.2.0 + firefox-profile: 4.7.0 fx-runner: 1.4.0 - mkdirp: 3.0.1 multimatch: 6.0.0 - mz: 2.7.0 node-notifier: 10.0.1 parse-json: 7.1.1 + pino: 9.7.0 promise-toolbox: 0.21.0 set-value: 4.1.0 source-map-support: 0.5.21 strip-bom: 5.0.0 - strip-json-comments: 5.0.1 - tmp: 0.2.3 - update-notifier: 6.0.2 - watchpack: 2.4.1 - ws: 8.18.0 + strip-json-comments: 5.0.2 + tmp: 0.2.5 + update-notifier: 7.3.1 + watchpack: 2.4.4 zip-dir: 2.0.0 transitivePeerDependencies: - - bufferutil - supports-color - - utf-8-validate webpack-virtual-modules@0.6.2: {} + when-exit@2.1.5: {} + when@3.7.7: {} which@1.2.4: @@ -5278,94 +4583,92 @@ snapshots: dependencies: isexe: 2.0.0 - widest-line@4.0.1: + why-is-node-running@2.3.0: dependencies: - string-width: 5.1.2 + siginfo: 2.0.0 + stackback: 0.0.2 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 winreg@0.0.12: {} + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.2 + strip-ansi: 7.2.0 + 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.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 string-width: 7.2.0 strip-ansi: 7.1.0 - wrappy@1.0.2: {} + ws@8.21.1: {} - write-file-atomic@3.0.3: + wsl-utils@0.3.1: dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 + is-wsl: 3.1.1 + powershell-utils: 0.1.0 - ws@8.18.0: {} - - wxt@0.20.0(@types/node@22.14.1)(jiti@2.4.2)(rollup@4.40.0): + wxt@0.20.27(@types/node@22.14.1)(jiti@2.7.0)(rollup@4.40.0): dependencies: '@1natsu/wait-element': 4.1.2 '@aklinker1/rollup-plugin-visualizer': 5.12.0(rollup@4.40.0) - '@webext-core/fake-browser': 1.3.2 - '@webext-core/isolated-element': 1.1.2 + '@webext-core/fake-browser': 1.5.2 + '@webext-core/isolated-element': 1.1.5 '@webext-core/match-patterns': 1.0.3 - '@wxt-dev/browser': 0.0.310 + '@wxt-dev/browser': 0.2.2 '@wxt-dev/storage': 1.1.1 async-mutex: 0.5.0 - c12: 3.0.3(magicast@0.3.5) + c12: 3.3.4(magicast@0.5.3) cac: 6.7.14 - chokidar: 4.0.3 - ci-info: 4.2.0 + chokidar: 5.0.0 + ci-info: 4.4.0 consola: 3.4.2 defu: 6.1.4 - dotenv: 16.5.0 - dotenv-expand: 12.0.1 - esbuild: 0.25.2 - fast-glob: 3.3.3 - filesize: 10.1.6 - fs-extra: 11.3.0 - get-port-please: 3.1.2 + dotenv-expand: 12.0.3 + esbuild: 0.27.7 + filesize: 11.0.22 + get-port-please: 3.2.0 giget: 2.0.0 - hookable: 5.5.3 - import-meta-resolve: 4.1.0 - is-wsl: 3.1.0 + hookable: 6.1.1 + import-meta-resolve: 4.2.0 + is-wsl: 3.1.1 json5: 2.2.3 jszip: 3.10.1 - linkedom: 0.18.9 - magicast: 0.3.5 - minimatch: 10.0.1 - nano-spawn: 0.2.0 + linkedom: 0.18.13 + magicast: 0.5.3 + nano-spawn: 2.1.0 + nanospinner: 1.2.2 normalize-path: 3.0.0 - nypm: 0.3.12 - ohash: 1.1.6 - open: 10.1.0 - ora: 8.2.0 - perfect-debounce: 1.0.0 - picocolors: 1.1.1 + nypm: 0.6.8 + ohash: 2.0.11 + open: 11.0.0 + perfect-debounce: 2.1.0 + picomatch: 4.0.5 prompts: 2.4.2 - publish-browser-extension: 3.0.0 + publish-browser-extension: 4.0.5 scule: 1.3.0 + tinyglobby: 0.2.17 unimport: 4.2.0 - vite: 6.2.6(@types/node@22.14.1)(jiti@2.4.2) - vite-node: 3.1.1(@types/node@22.14.1)(jiti@2.4.2) - web-ext-run: 0.2.2 + vite: 8.1.5(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) + vite-node: 6.0.0(@types/node@22.14.1)(esbuild@0.27.7)(jiti@2.7.0) + web-ext-run: 0.2.4 transitivePeerDependencies: - '@types/node' - - bufferutil + - '@vitejs/devtools' + - canvas - jiti - less - - lightningcss - rollup - sass - sass-embedded @@ -5374,12 +4677,11 @@ snapshots: - supports-color - terser - tsx - - utf-8-validate - yaml xdg-basedir@5.1.0: {} - xml2js@0.5.0: + xml2js@0.6.2: dependencies: sax: 1.4.1 xmlbuilder: 11.0.1 @@ -5388,22 +4690,8 @@ snapshots: y18n@5.0.8: {} - yallist@3.1.1: {} - - yargs-parser@20.2.9: {} - yargs-parser@21.1.1: {} - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -5414,14 +4702,9 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - zip-dir@2.0.0: dependencies: async: 3.2.6 jszip: 3.10.1 - zod@3.24.2: {} + zod@4.4.3: {} diff --git a/public/managed-storage-schema.json b/public/managed-storage-schema.json new file mode 100644 index 0000000..dffdf26 --- /dev/null +++ b/public/managed-storage-schema.json @@ -0,0 +1,48 @@ +{ + "$schema": "http://json-schema.org/draft-03/schema#", + "type": "object", + "properties": { + "bridgeTransport": { + "title": "Bridge transport", + "description": "Lock the extension to native or loopback WebSocket transport.", + "type": "string", + "enum": ["native", "websocket"] + }, + "bridgeEndpoint": { + "title": "Bridge endpoint", + "description": "Managed loopback WebSocket endpoint.", + "type": "string" + }, + "nativeHost": { + "title": "Native Messaging host", + "type": "string" + }, + "autoConnect": { + "title": "Connect automatically", + "type": "boolean" + }, + "disableWebSocket": { + "title": "Require Native Messaging", + "type": "boolean" + }, + "floatingPanelEnabled": { + "title": "Enable the page floating panel", + "type": "boolean" + }, + "maxGrantMinutes": { + "title": "Maximum grant duration in minutes", + "type": "integer", + "minimum": 5, + "maximum": 1440 + }, + "grantAllowedOrigins": { + "title": "Origins that may be shared with an Agent", + "type": "array", + "items": { "type": "string" } + }, + "allowProgramEval": { + "title": "Allow program Eval grants", + "type": "boolean" + } + } +} diff --git a/scripts/audit-build.mjs b/scripts/audit-build.mjs new file mode 100644 index 0000000..50ab5a4 --- /dev/null +++ b/scripts/audit-build.mjs @@ -0,0 +1,94 @@ +import { access, readFile, stat } from 'node:fs/promises'; +import { join, resolve } from 'node:path'; +import { gzipSync } from 'node:zlib'; + +const root = resolve(import.meta.dirname, '..'); +const MIB = 1024 * 1024; +// Extension Service Workers do not support runtime import(). Bridge v3 identity verification must stay in the startup bundle. +const BRIDGE_BACKGROUND_BUDGET = 144 * 1024; +const BRIDGE_BACKGROUND_GZIP_BUDGET = 44 * 1024; + +const targets = [ + { name: 'store', dir: '.output/chrome-mv3-store', contentBudget: 12 * 1024, backgroundBudget: BRIDGE_BACKGROUND_BUDGET, backgroundGzipBudget: BRIDGE_BACKGROUND_GZIP_BUDGET, totalBudget: MIB, directEval: false, userScripts: true, execution: 'user-scripts' }, + { name: 'enterprise', dir: '.output/chrome-mv3-enterprise', contentBudget: 16 * 1024, backgroundBudget: BRIDGE_BACKGROUND_BUDGET, backgroundGzipBudget: BRIDGE_BACKGROUND_GZIP_BUDGET, totalBudget: MIB, directEval: true, userScripts: true, execution: 'user-scripts+injected-fallback' }, + { name: 'firefox', dir: '.output/firefox-mv2', contentBudget: 16 * 1024, backgroundBudget: BRIDGE_BACKGROUND_BUDGET, backgroundGzipBudget: BRIDGE_BACKGROUND_GZIP_BUDGET, totalBudget: MIB, directEval: true, userScripts: false, execution: 'injected-bridge' }, + { name: 'firefox-amo', dir: '.output/firefox-mv3-store', contentBudget: 12 * 1024, backgroundBudget: BRIDGE_BACKGROUND_BUDGET, backgroundGzipBudget: BRIDGE_BACKGROUND_GZIP_BUDGET, totalBudget: MIB, directEval: false, userScripts: false, execution: 'invoke-only' }, +]; + +async function fileSize(path) { + return (await stat(path)).size; +} + +async function exists(path) { + try { + await access(path); + return true; + } catch { + return false; + } +} + +function assert(condition, message) { + if (!condition) throw new Error(message); +} + +async function directorySize(path) { + const { readdir } = await import('node:fs/promises'); + let total = 0; + for (const entry of await readdir(path, { withFileTypes: true })) { + const child = join(path, entry.name); + total += entry.isDirectory() ? await directorySize(child) : await fileSize(child); + } + return total; +} + +const report = []; +for (const target of targets) { + const isFirefox = target.name.startsWith('firefox'); + const output = resolve(root, target.dir); + assert(await exists(output), `${target.name} 产物不存在,请先运行对应构建命令`); + const manifest = JSON.parse(await readFile(join(output, 'manifest.json'), 'utf8')); + const contentBytes = await fileSize(join(output, 'content-scripts/agent.js')); + const backgroundSource = await readFile(join(output, 'background.js')); + const backgroundBytes = backgroundSource.byteLength; + const backgroundGzipBytes = gzipSync(backgroundSource).byteLength; + const observerBytes = await fileSize(join(output, 'page-observer-main-world.js')); + const totalBytes = await directorySize(output); + const directEvalExists = await exists(join(output, 'page-main-world.js')); + const resources = (manifest.web_accessible_resources || []).flatMap((entry) => typeof entry === 'string' ? [entry] : entry.resources || []); + const dynamicResourceGroup = (manifest.web_accessible_resources || []).find((entry) => typeof entry !== 'string' && entry.resources?.includes('floating.html')); + + assert(contentBytes <= target.contentBudget, `${target.name} 常驻 content script ${contentBytes}B 超过预算 ${target.contentBudget}B`); + assert(backgroundBytes <= target.backgroundBudget, `${target.name} background ${backgroundBytes}B 超过 ${target.backgroundBudget / 1024}KiB 原始预算`); + assert(backgroundGzipBytes <= target.backgroundGzipBudget, `${target.name} background gzip ${backgroundGzipBytes}B 超过 ${target.backgroundGzipBudget / 1024}KiB 预算`); + assert(observerBytes <= 12 * 1024, `${target.name} MAIN-world observer ${observerBytes}B 超过 12KiB 预算`); + assert(totalBytes <= target.totalBudget, `${target.name} 总产物 ${totalBytes}B 超过 ${target.totalBudget / MIB}MiB 预算`); + assert(directEvalExists === target.directEval, `${target.name} page-main-world.js 存在状态不符合构建策略`); + assert(resources.includes('page-main-world.js') === target.directEval, `${target.name} page-main-world.js 暴露状态不符合构建策略`); + assert((manifest.permissions || []).includes('userScripts') === target.userScripts, `${target.name} userScripts 权限不符合构建策略`); + if (target.name === 'store' || target.name === 'firefox-amo') { + assert(!backgroundSource.toString().includes('(0,eval)'), `${target.name} background 不得包含间接 Eval 实现`); + } + assert((manifest.permissions || []).includes('webRequest'), `${target.name} 缺少网络捕获所需 webRequest 权限`); + assert((manifest.permissions || []).includes('webNavigation'), `${target.name} 缺少 frame/document 生命周期所需 webNavigation 权限`); + assert(!(manifest.permissions || []).includes('activeTab'), `${target.name} 不应申请未使用的 activeTab 权限`); + assert(!(manifest.permissions || []).includes('nativeMessaging') && (manifest.optional_permissions || []).includes('nativeMessaging'), `${target.name} Native Messaging 必须按需授权`); + assert((manifest.permissions || []).includes(isFirefox ? 'webRequestBlocking' : 'webRequestAuthProvider'), `${target.name} 缺少代理认证权限`); + assert(manifest.storage?.managed_schema === 'managed-storage-schema.json', `${target.name} 缺少企业 managed storage schema`); + assert(await exists(join(output, 'managed-storage-schema.json')), `${target.name} managed storage schema 未打包`); + if (!isFirefox) assert(!(manifest.permissions || []).includes('webRequestBlocking'), `${target.name} 不应申请阻断或修改网络请求的 webRequestBlocking 权限`); + assert(resources.includes('floating.html'), `${target.name} 没有公开按需浮动页`); + if (manifest.manifest_version === 3) assert(dynamicResourceGroup?.use_dynamic_url === true, `${target.name} 浮动页必须使用动态资源 URL`); + + report.push({ + target: target.name, + contentScriptKiB: Number((contentBytes / 1024).toFixed(2)), + backgroundKiB: Number((backgroundBytes / 1024).toFixed(2)), + backgroundGzipKiB: Number((backgroundGzipBytes / 1024).toFixed(2)), + observerKiB: Number((observerBytes / 1024).toFixed(2)), + totalKiB: Number((totalBytes / 1024).toFixed(2)), + execution: target.execution, + }); +} + +console.log(JSON.stringify(report, null, 2)); diff --git a/scripts/dev-wsl.mjs b/scripts/dev-wsl.mjs new file mode 100644 index 0000000..2b37264 --- /dev/null +++ b/scripts/dev-wsl.mjs @@ -0,0 +1,81 @@ +import { createInterface } from 'node:readline'; +import { spawn } from 'node:child_process'; +import { access, mkdir, realpath } from 'node:fs/promises'; +import { constants } from 'node:fs'; +import { resolve } from 'node:path'; + +const root = process.cwd(); +const output = resolve(root, '.output/chrome-mv3-dev'); +const manifest = resolve(output, 'manifest.json'); +const profile = resolve(root, '.wxt/chrome-wsl-profile'); +const chrome = process.env.CHROME_PATH || '/usr/bin/google-chrome'; + +await access(chrome, constants.X_OK).catch(() => { + throw new Error(`Chrome is not executable: ${chrome}. Set CHROME_PATH to override it.`); +}); +await mkdir(profile, { recursive: true }); +const resolvedChrome = await realpath(chrome); +const isBrandedChrome = resolvedChrome.startsWith('/opt/google/chrome/'); + +const wxt = spawn(process.execPath, [resolve(root, 'node_modules/wxt/bin/wxt.mjs')], { + cwd: root, + env: process.env, + stdio: ['inherit', 'pipe', 'pipe'], +}); + +const pipeLines = (stream, destination) => { + const reader = createInterface({ input: stream }); + reader.on('line', (line) => { + if (!line.includes('Cannot open browser when using WSL')) destination.write(`${line}\n`); + }); +}; +pipeLines(wxt.stdout, process.stdout); +pipeLines(wxt.stderr, process.stderr); + +const waitForManifest = async () => { + for (let attempt = 0; attempt < 200; attempt += 1) { + if (wxt.exitCode !== null) throw new Error(`WXT exited before producing ${manifest}`); + try { + await access(manifest, constants.R_OK); + return; + } catch { + await new Promise((resolveWait) => setTimeout(resolveWait, 100)); + } + } + throw new Error(`Timed out waiting for ${manifest}`); +}; + +let chromeProcess; +const shutdown = async (signal) => { + if (chromeProcess?.exitCode === null) chromeProcess.kill(signal); + if (wxt.exitCode === null) wxt.kill(signal); +}; +process.once('SIGINT', () => void shutdown('SIGINT')); +process.once('SIGTERM', () => void shutdown('SIGTERM')); + +try { + await waitForManifest(); + const chromeArgs = [ + `--user-data-dir=${profile}`, + '--disable-gpu', + '--no-first-run', + '--no-default-browser-check', + ]; + if (!isBrandedChrome || process.env.WXT_AUTO_LOAD_EXTENSION === '1') { + chromeArgs.push(`--disable-extensions-except=${output}`, `--load-extension=${output}`, 'about:blank'); + } else { + chromeArgs.push('chrome://extensions'); + } + chromeProcess = spawn(chrome, chromeArgs, { cwd: root, env: process.env, stdio: 'inherit' }); + if (isBrandedChrome && process.env.WXT_AUTO_LOAD_EXTENSION !== '1') { + process.stdout.write(`\nOpened official Chrome with the persistent WXT profile.\nChrome 137+ ignores --load-extension in branded builds. On first run, enable Developer mode and load:\n${output}\nProfile: ${profile}\n`); + } else { + process.stdout.write(`\nOpened ${chrome} with the WXT development extension.\nProfile: ${profile}\n`); + } +} catch (error) { + await shutdown('SIGTERM'); + throw error; +} + +await new Promise((resolveExit) => wxt.once('exit', resolveExit)); +if (chromeProcess?.exitCode === null) chromeProcess.kill('SIGTERM'); diff --git a/scripts/resolve-chromium.mjs b/scripts/resolve-chromium.mjs new file mode 100644 index 0000000..908e4a9 --- /dev/null +++ b/scripts/resolve-chromium.mjs @@ -0,0 +1,44 @@ +import { constants } from 'node:fs'; +import { access, readdir } from 'node:fs/promises'; +import { homedir } from 'node:os'; +import { join } from 'node:path'; +import { spawnSync } from 'node:child_process'; + +async function executable(path) { + if (!path) return false; + try { + await access(path, constants.X_OK); + return true; + } catch { + return false; + } +} + +export async function resolveChromiumPath() { + for (const candidate of [process.env.CHROMIUM_PATH, process.env.CHROME_PATH]) { + if (await executable(candidate)) return candidate; + } + + const cacheRoot = process.env.PLAYWRIGHT_BROWSERS_PATH || join(homedir(), '.cache', 'ms-playwright'); + let entries = []; + try { + entries = (await readdir(cacheRoot, { withFileTypes: true })) + .filter((entry) => entry.isDirectory() && entry.name.startsWith('chromium-')) + .map((entry) => entry.name) + .sort((left, right) => Number(right.slice(9)) - Number(left.slice(9))); + } catch { + // The final error below lists the supported configuration options. + } + for (const entry of entries) { + for (const relative of ['chrome-linux64/chrome', 'chrome-linux/chrome']) { + const candidate = join(cacheRoot, entry, relative); + if (await executable(candidate)) return candidate; + } + } + + for (const command of ['google-chrome-for-testing', 'chromium', 'chromium-browser']) { + const resolved = spawnSync('which', [command], { encoding: 'utf8' }).stdout.trim(); + if (await executable(resolved)) return resolved; + } + throw new Error('Unpacked-capable Chromium not found. Set CHROMIUM_PATH or install Chromium/Playwright Chromium.'); +} diff --git a/scripts/verify-native-host.mjs b/scripts/verify-native-host.mjs new file mode 100644 index 0000000..78197e5 --- /dev/null +++ b/scripts/verify-native-host.mjs @@ -0,0 +1,241 @@ +import { spawnSync } from 'node:child_process'; +import { randomBytes, webcrypto } from 'node:crypto'; +import { cp, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { createServer } from 'node:http'; +import { tmpdir } from 'node:os'; +import { dirname, join, resolve } from 'node:path'; +import { chromium } from 'playwright-core'; +import { WebSocketServer } from 'ws'; +import { resolveChromiumPath } from './resolve-chromium.mjs'; + +const root = resolve(import.meta.dirname, '..'); +const yakRoot = resolve(process.env.YAK_REPO || root, process.env.YAK_REPO ? '.' : '../../go/yaklang'); +const extensionPath = resolve(root, process.env.EXTENSION_PATH || '.output/chrome-mv3-store'); +const executablePath = await resolveChromiumPath(); +const temporary = await mkdtemp(join(tmpdir(), 'yakit-native-host-e2e-')); +const home = join(temporary, 'home'); +const profile = join(temporary, 'profile'); +const hostBinary = join(temporary, 'yakit-browser-agent-host'); +const testExtensionPath = join(temporary, 'extension'); +const hostName = 'com.yaklang.browser_agent'; + +const packagedManifest = JSON.parse(await readFile(join(extensionPath, 'manifest.json'), 'utf8')); +if (!packagedManifest.optional_permissions?.includes('nativeMessaging') || packagedManifest.permissions?.includes('nativeMessaging')) { + throw new Error('Native Messaging is not packaged as an optional permission'); +} +// Chrome's optional-permission prompt is browser chrome and cannot be accepted by +// Playwright. Pre-grant it only in a disposable copy so the native transport itself +// can still be exercised through the real extension and browser APIs. +await cp(extensionPath, testExtensionPath, { recursive: true }); +const testManifest = structuredClone(packagedManifest); +testManifest.permissions = [...new Set([...(testManifest.permissions || []), 'nativeMessaging'])]; +testManifest.optional_permissions = (testManifest.optional_permissions || []).filter((value) => value !== 'nativeMessaging'); +await writeFile(join(testExtensionPath, 'manifest.json'), JSON.stringify(testManifest, null, 2)); + +const build = spawnSync('go', ['build', '-o', hostBinary, './common/browser/nativehostcmd'], { + cwd: yakRoot, encoding: 'utf8', env: process.env, +}); +if (build.status !== 0) throw new Error(`Native Host build failed:\n${build.stderr || build.stdout}`); + +const bridgeHTTPServer = createServer(); +const pairingServer = new WebSocketServer({ noServer: true }); +const bridgeServer = new WebSocketServer({ noServer: true }); +bridgeHTTPServer.on('upgrade', (request, socket, head) => { + const pathname = new URL(request.url || '/', 'http://127.0.0.1').pathname; + const target = pathname === '/pairing' ? pairingServer : pathname === '/extension' ? bridgeServer : undefined; + if (!target) return socket.destroy(); + target.handleUpgrade(request, socket, head, (webSocket) => target.emit('connection', webSocket, request)); +}); +await new Promise((resolveListen) => bridgeHTTPServer.listen(0, '127.0.0.1', resolveListen)); +const address = bridgeHTTPServer.address(); +const endpoint = `ws://127.0.0.1:${address.port}/extension`; +const protocolVersion = 3; +const engineIdentityId = 'native-e2e-engine-identity'; +const engineInstanceId = 'native-e2e-engine-instance'; +const engineKeys = await webcrypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']); +const rawEngineJWK = await webcrypto.subtle.exportKey('jwk', engineKeys.publicKey); +const enginePublicKey = { kty: 'EC', crv: 'P-256', x: rawEngineJWK.x, y: rawEngineJWK.y }; +let pairedClient; +let authenticatedConnections = 0; +let resolveHello; +const helloReceived = new Promise((resolveMessage) => { resolveHello = resolveMessage; }); + +const toBase64URL = (value) => Buffer.from(value).toString('base64url'); +const engineChallengePayload = (challenge, timestamp) => ['yak-browser-bridge-v3', 'engine-challenge', engineIdentityId, engineInstanceId, challenge, String(timestamp)].join('\n'); +const clientAuthPayload = (origin, challenge, auth) => [ + 'yak-browser-bridge-v3', 'client-auth', origin, engineIdentityId, engineInstanceId, challenge, + auth.installationId || '', auth.client || '', auth.version || '', [...(auth.capabilities || [])].sort().join(','), + auth.taskId || '', auth.grantId || '', auth.resumeSessionId || '', +].join('\n'); + +pairingServer.on('connection', (socket, request) => socket.once('message', async (raw) => { + const pairing = JSON.parse(raw.toString()); + if (pairing.type !== 'pair_request' || pairing.protocolVersion !== protocolVersion) return socket.close(1008, 'invalid pairing request'); + const requestId = 'native-e2e-pairing'; + const serverNonce = toBase64URL(randomBytes(32)); + const transcript = [ + 'yak-browser-pairing-v1', engineIdentityId, requestId, request.headers.origin, pairing.installationId, + pairing.nonce, serverNonce, pairing.publicKey.kty, pairing.publicKey.crv, pairing.publicKey.x, pairing.publicKey.y, + ].join('\n'); + const digest = Buffer.from(await webcrypto.subtle.digest('SHA-256', Buffer.from(transcript))); + const code = String(digest.readBigUInt64BE() % 1_000_000n).padStart(6, '0'); + pairedClient = { installationId: pairing.installationId, publicKey: pairing.publicKey }; + socket.send(JSON.stringify({ + type: 'pair_pending', protocolVersion, requestId, serverNonce, engineIdentityId, code, + expiresAt: Date.now() + 60_000, publicKey: enginePublicKey, + })); + setTimeout(() => socket.send(JSON.stringify({ + type: 'pair_approved', requestId, deviceId: 'native-e2e-device', engineIdentityId, publicKey: enginePublicKey, + })), 50); +})); + +bridgeServer.on('connection', async (socket, request) => { + const challenge = toBase64URL(randomBytes(32)); + const timestamp = Date.now(); + socket.send(JSON.stringify({ + type: 'challenge', protocolVersion, challenge, timestamp, engineIdentityId, engineInstanceId, publicKey: enginePublicKey, + signature: toBase64URL(await webcrypto.subtle.sign( + { name: 'ECDSA', hash: 'SHA-256' }, engineKeys.privateKey, Buffer.from(engineChallengePayload(challenge, timestamp)), + )), + })); + socket.once('message', (raw) => void (async () => { + const hello = JSON.parse(raw.toString()); + if (!request.headers.origin?.startsWith('chrome-extension://') || hello.type !== 'auth' || hello.protocolVersion !== protocolVersion || !hello.installationId || hello.challenge !== challenge || !pairedClient) { + socket.close(1008, 'invalid native e2e handshake'); + return; + } + const clientKey = await webcrypto.subtle.importKey('jwk', pairedClient.publicKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']); + const verified = await webcrypto.subtle.verify( + { name: 'ECDSA', hash: 'SHA-256' }, clientKey, Buffer.from(hello.signature, 'base64url'), + Buffer.from(clientAuthPayload(request.headers.origin, challenge, hello)), + ); + if (!verified || pairedClient.installationId !== hello.installationId) return socket.close(1008, 'invalid native e2e signature'); + socket.send(JSON.stringify({ + type: 'hello_ack', protocolVersion, version: 'native-e2e-engine', capabilities: [], + sessionId: 'native-e2e-session', engineIdentityId, engineInstanceId, + connectionId: 'native-e2e-connection', resumed: false, + })); + socket.on('message', (payload) => { + const message = JSON.parse(payload.toString()); + if (message.type === 'ping') socket.send(JSON.stringify({ + type: 'pong', id: message.id, sequence: message.sequence, timestamp: message.timestamp, replyTimestamp: Date.now(), + })); + }); + authenticatedConnections += 1; + if (authenticatedConnections >= 2) resolveHello({ hello, origin: request.headers.origin }); + })()); +}); + +await mkdir(join(home, '.config', 'yakit'), { recursive: true }); +await writeFile(join(home, '.config', 'yakit', 'browser-agent-native-host.json'), JSON.stringify({ endpoint })); + +let context; +try { + const launch = () => chromium.launchPersistentContext(profile, { + executablePath, + headless: true, + env: { ...process.env, HOME: home }, + args: [`--disable-extensions-except=${testExtensionPath}`, `--load-extension=${testExtensionPath}`, '--no-first-run'], + }); + context = await launch(); + let worker = context.serviceWorkers()[0]; + if (!worker) worker = await context.waitForEvent('serviceworker', { timeout: 15_000 }); + const extensionId = new URL(worker.url()).host; + const manifest = { + name: hostName, + description: 'Yakit Browser Agent Native Host E2E', + path: hostBinary, + type: 'stdio', + allowed_origins: [`chrome-extension://${extensionId}/`], + }; + const manifestDirectories = [ + ...['google-chrome', 'google-chrome-for-testing', 'chromium'].map((product) => join(home, '.config', product, 'NativeMessagingHosts')), + join(profile, 'NativeMessagingHosts'), + ]; + for (const directory of manifestDirectories) { + const path = join(directory, `${hostName}.json`); + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, JSON.stringify(manifest)); + } + + // Chromium caches native-host registrations at process startup. + await context.close(); + context = await launch(); + worker = context.serviceWorkers()[0]; + if (!worker) worker = await context.waitForEvent('serviceworker', { timeout: 15_000 }); + const restartedExtensionId = new URL(worker.url()).host; + if (restartedExtensionId !== extensionId) throw new Error('Extension ID changed after Native Host registration'); + + const options = await context.newPage(); + await options.goto(`chrome-extension://${extensionId}/options.html#engine`); + try { + await options.evaluate(async ({ bridgeEndpoint }) => { + const send = async (action, payload) => { + const response = await chrome.runtime.sendMessage({ action, payload }); + if (!response?.ok) throw new Error(response?.error || action); + return response.data; + }; + const state = await send('state.get'); + await send('bridge.config.save', { + transport: 'websocket', nativeHost: 'com.yaklang.browser_agent', endpoint: bridgeEndpoint, + autoConnect: false, installationId: state.bridge.installationId, + }); + await send('bridge.pair'); + for (let attempt = 0; attempt < 50; attempt += 1) { + const [next, status] = await Promise.all([send('state.get'), send('bridge.status')]); + if (next.bridge.pairedEngine && status.state === 'connected') { + await send('bridge.disconnect'); + await send('bridge.config.save', { ...next.bridge, transport: 'native', autoConnect: false }); + await send('bridge.connect'); + return; + } + await new Promise((resolve) => setTimeout(resolve, 50)); + } + throw new Error('Browser extension pairing did not complete'); + }, { bridgeEndpoint: endpoint }); + } catch (error) { + const diagnostics = await options.evaluate(async () => ({ + body: document.body.innerText, + permissions: await chrome.permissions.getAll(), + bridge: await chrome.runtime.sendMessage({ action: 'bridge.status' }), + })); + throw new Error(`Native Host pairing and settings failed: ${JSON.stringify(diagnostics)}`, { cause: error }); + } + let connection; + try { + connection = await Promise.race([ + helloReceived, + new Promise((_, reject) => setTimeout(() => reject(new Error('Native Host did not reach Yak Bridge')), 15_000)), + ]); + } catch (error) { + const diagnostics = await options.evaluate(async () => ({ + body: document.body.innerText, + permissions: await chrome.permissions.getAll(), + bridge: await chrome.runtime.sendMessage({ action: 'bridge.status' }), + })); + throw new Error(`Native Host transport failed: ${JSON.stringify(diagnostics)}`, { cause: error }); + } + const status = await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'bridge.status' }); + if (!response?.ok) throw new Error(response?.error || 'bridge.status'); + return response.data; + }); + if (status.state !== 'connected' || status.engineInstanceId !== 'native-e2e-engine-instance' || status.connectionId !== 'native-e2e-connection') { + throw new Error(`Native Host identity did not reach the extension: ${JSON.stringify(status)}`); + } + console.log(JSON.stringify({ + extensionId, + endpoint, + permissionFixture: 'pre-granted only in temporary E2E copy; production package remains optional', + connection, + status, + }, null, 2)); +} finally { + await context?.close(); + for (const client of bridgeServer.clients) client.terminate(); + for (const client of pairingServer.clients) client.terminate(); + bridgeServer.close(); + pairingServer.close(); + bridgeHTTPServer.close(); + await rm(temporary, { recursive: true, force: true }); +} diff --git a/scripts/verify-ui.mjs b/scripts/verify-ui.mjs new file mode 100644 index 0000000..bededb1 --- /dev/null +++ b/scripts/verify-ui.mjs @@ -0,0 +1,1357 @@ +import { mkdtemp, mkdir, readFile, rm } from 'node:fs/promises'; +import { randomBytes, webcrypto } from 'node:crypto'; +import { createServer } from 'node:http'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { chromium } from 'playwright-core'; +import { WebSocketServer } from 'ws'; +import { resolveChromiumPath } from './resolve-chromium.mjs'; + +const root = resolve(import.meta.dirname, '..'); +const extensionPath = resolve(root, process.env.EXTENSION_PATH || '.output/chrome-mv3'); +const extensionManifest = JSON.parse(await readFile(resolve(extensionPath, 'manifest.json'), 'utf8')); +const shouldEnableUserScripts = process.env.ENABLE_USER_SCRIPTS !== '0' && extensionManifest.permissions?.includes('userScripts'); +const artifacts = resolve(root, '.artifacts/ui'); +const executablePath = await resolveChromiumPath(); +const userDataDir = await mkdtemp(join(tmpdir(), 'yakit-extension-')); +await mkdir(artifacts, { recursive: true }); + +const server = createServer((request, response) => { + if (request.url?.startsWith('/api/session')) { + const chunks = []; + request.on('data', (chunk) => chunks.push(chunk)); + request.on('end', () => { + response.setHeader('content-type', 'application/json; charset=utf-8'); + response.setHeader('x-yakit-e2e-response', 'captured'); + response.end(JSON.stringify({ ok: true, receivedBytes: Buffer.concat(chunks).length })); + }); + return; + } + if (request.url?.startsWith('/frame-')) { + const cross = request.url.startsWith('/frame-cross'); + response.setHeader('content-type', 'text/html; charset=utf-8'); + response.end(`${cross ? 'Cross Origin Account Frame' : 'Same Origin Billing Frame'}

${cross ? 'Cross account' : 'Billing'}

`); + return; + } + if (request.url?.startsWith('/strict-csp')) { + response.setHeader('content-type', 'text/html; charset=utf-8'); + response.setHeader('content-security-policy', "default-src 'none'; script-src 'none'; style-src 'none'; frame-src 'none'"); + response.end('Strict CSP Page

Strict CSP Page

'); + return; + } + response.setHeader('content-type', 'text/html; charset=utf-8'); + response.setHeader('set-cookie', 'yakit_e2e_session=authenticated; Path=/; HttpOnly; SameSite=Lax'); + const port = server.address()?.port; + response.end(` + Authenticated Security Console

Authenticated Security Console

Local page for extension UI and main-world execution verification.

`); +}); +const pageSocketServer = new WebSocketServer({ server, path: '/page-socket' }); +pageSocketServer.on('connection', (socket) => socket.on('message', (message) => socket.send(`echo:${message.toString()}`))); +await new Promise((resolveListen) => server.listen(0, '127.0.0.1', resolveListen)); +const address = server.address(); +const testUrl = `http://127.0.0.1:${address.port}/authenticated`; +const insecureTestUrl = `http://yakit-insecure.test:${address.port}/insecure`; +const bridgeHTTPServer = createServer(); +const pairingServer = new WebSocketServer({ noServer: true }); +const bridgeServer = new WebSocketServer({ noServer: true }); +bridgeHTTPServer.on('upgrade', (request, socket, head) => { + const pathname = new URL(request.url || '/', 'http://127.0.0.1').pathname; + const target = pathname === '/pairing' ? pairingServer : pathname === '/extension' ? bridgeServer : undefined; + if (!target) { + socket.destroy(); + return; + } + target.handleUpgrade(request, socket, head, (webSocket) => target.emit('connection', webSocket, request)); +}); +await new Promise((resolveListen) => bridgeHTTPServer.listen(0, '127.0.0.1', resolveListen)); +const bridgeAddress = bridgeHTTPServer.address(); +const bridgeEndpoint = `ws://127.0.0.1:${bridgeAddress.port}/extension`; +const bridgeProtocolVersion = 3; +const engineIdentityId = 'e2e-engine-identity'; +const engineInstanceId = 'e2e-engine-instance'; +const engineKeys = await webcrypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']); +const engineJWKRaw = await webcrypto.subtle.exportKey('jwk', engineKeys.publicKey); +const enginePublicKey = { kty: 'EC', crv: 'P-256', x: engineJWKRaw.x, y: engineJWKRaw.y }; +let pairedClient; + +function bytesToBase64URL(value) { + return Buffer.from(value).toString('base64url'); +} + +async function sha256(value) { + return new Uint8Array(await webcrypto.subtle.digest('SHA-256', Buffer.from(value))); +} + +async function signEngine(value) { + return bytesToBase64URL(await webcrypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, engineKeys.privateKey, Buffer.from(value))); +} + +function engineChallengePayload(challenge, timestamp) { + return ['yak-browser-bridge-v3', 'engine-challenge', engineIdentityId, engineInstanceId, challenge, String(timestamp)].join('\n'); +} + +function clientAuthPayload(origin, challenge, auth) { + return [ + 'yak-browser-bridge-v3', 'client-auth', origin, engineIdentityId, engineInstanceId, challenge, + auth.installationId || '', auth.client || '', auth.version || '', [...(auth.capabilities || [])].sort().join(','), + auth.taskId || '', auth.grantId || '', auth.resumeSessionId || '', + ].join('\n'); +} + +async function verifyClientAuth(origin, challenge, auth) { + if (!pairedClient || pairedClient.installationId !== auth.installationId) return false; + const key = await webcrypto.subtle.importKey('jwk', pairedClient.publicKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']); + return await webcrypto.subtle.verify( + { name: 'ECDSA', hash: 'SHA-256' }, key, Buffer.from(auth.signature, 'base64url'), Buffer.from(clientAuthPayload(origin, challenge, auth)), + ); +} + +pairingServer.on('connection', (socket, request) => socket.once('message', async (raw) => { + try { + const pairing = JSON.parse(raw.toString()); + if (pairing.type !== 'pair_request' || pairing.protocolVersion !== bridgeProtocolVersion) throw new Error('invalid pairing request'); + const requestId = 'e2e-pairing-request'; + const serverNonce = bytesToBase64URL(randomBytes(32)); + const transcript = [ + 'yak-browser-pairing-v1', engineIdentityId, requestId, request.headers.origin, pairing.installationId, + pairing.nonce, serverNonce, pairing.publicKey.kty, pairing.publicKey.crv, pairing.publicKey.x, pairing.publicKey.y, + ].join('\n'); + const digest = await sha256(transcript); + const code = String(Buffer.from(digest.subarray(0, 8)).readBigUInt64BE() % 1_000_000n).padStart(6, '0'); + pairedClient = { installationId: pairing.installationId, publicKey: pairing.publicKey, origin: request.headers.origin }; + socket.send(JSON.stringify({ + type: 'pair_pending', protocolVersion: bridgeProtocolVersion, requestId, serverNonce, + engineIdentityId, code, expiresAt: Date.now() + 60_000, publicKey: enginePublicKey, + })); + setTimeout(() => socket.send(JSON.stringify({ + type: 'pair_approved', requestId, deviceId: 'e2e-browser-device', engineIdentityId, publicKey: enginePublicKey, + })), 50); + } catch (error) { + socket.send(JSON.stringify({ type: 'pair_error', message: error.message })); + } +})); +let resolveWebFuzzerOpen; +const webFuzzerOpenRequest = new Promise((resolveRequest) => { resolveWebFuzzerOpen = resolveRequest; }); +let resolvePocGenerate; +const pocGenerateRequest = new Promise((resolveRequest) => { resolvePocGenerate = resolveRequest; }); +let resolveAnalysisPrepare; +const analysisPrepareRequest = new Promise((resolveRequest) => { resolveAnalysisPrepare = resolveRequest; }); +const bridgeConnection = new Promise((resolveConnection, rejectConnection) => { + bridgeServer.once('connection', async (socket, request) => { + const challenge = bytesToBase64URL(randomBytes(32)); + const timestamp = Date.now(); + socket.send(JSON.stringify({ + type: 'challenge', protocolVersion: bridgeProtocolVersion, challenge, timestamp, + engineIdentityId, engineInstanceId, publicKey: enginePublicKey, + signature: await signEngine(engineChallengePayload(challenge, timestamp)), + })); + socket.once('message', (raw) => { + void (async () => { + const hello = JSON.parse(raw.toString()); + if (hello.type !== 'auth' || hello.challenge !== challenge || !(await verifyClientAuth(request.headers.origin, challenge, hello))) { + throw new Error('invalid Bridge v3 client authentication'); + } + socket.send(JSON.stringify({ + type: 'hello_ack', + protocolVersion: bridgeProtocolVersion, + version: 'e2e-engine', + capabilities: ['yakit.web_fuzzer.open', 'yakit.poc.generate', 'yakit.browser_request.prepare_analysis'], + sessionId: 'e2e-session', + engineIdentityId, + engineInstanceId, + connectionId: 'e2e-connection', + taskId: hello.taskId, + grantId: hello.grantId, + resumed: Boolean(hello.resumeSessionId), + })); + socket.on('message', (requestRaw) => { + try { + const message = JSON.parse(requestRaw.toString()); + if (message.type === 'ping') { + socket.send(JSON.stringify({ type: 'pong', id: message.id, sequence: message.sequence, timestamp: message.timestamp, replyTimestamp: Date.now() })); + return; + } + if (message.type !== 'request') return; + if (message.method === 'yakit.web_fuzzer.open') { + resolveWebFuzzerOpen(message); + socket.send(JSON.stringify({ id: message.id, type: 'response', result: { pageId: 'e2e-fuzzer-page', tabName: message.params?.tabName || 'Browser Request' } })); + } else if (message.method === 'yakit.poc.generate') { + resolvePocGenerate(message); + socket.send(JSON.stringify({ id: message.id, type: 'response', result: { language: 'yak', fileName: 'browser-e2e.yak', code: 'packet = codec.DecodeBase64("e2e")\nrsp, req, err = poc.HTTP(packet)' } })); + } else if (message.method === 'yakit.browser_request.prepare_analysis') { + resolveAnalysisPrepare(message); + socket.send(JSON.stringify({ + id: message.id, + type: 'response', + result: { + request: { method: 'POST', scheme: 'http', host: '127.0.0.1', path: '/api/session', contentType: 'application/json', queryKeys: ['source'], headerNames: ['Cookie', 'X-Yakit-E2e'], cookieNames: ['yakit_e2e_session'], bodyKeys: ['marker'], bodyBytes: 38 }, + signals: [{ location: 'header', name: 'Cookie', category: 'cookie' }], + observations: message.params?.observations || [], + valuePolicy: 'values omitted', + recommendedChecks: ['authorization boundary'], + }, + })); + } + } catch { + // The main E2E assertions report malformed Bridge traffic. + } + }); + resolveConnection({ socket, hello }); + })().catch((error) => { + rejectConnection(error); + }); + }); + }); +}); + +function nextBridgeResponse(socket, id) { + return new Promise((resolveResponse, rejectResponse) => { + const onMessage = (raw) => { + try { + const message = JSON.parse(raw.toString()); + if (message.id !== id) return; + socket.off('message', onMessage); + resolveResponse(message); + } catch (error) { + socket.off('message', onMessage); + rejectResponse(error); + } + }; + socket.on('message', onMessage); + }); +} + +function nextBridgeEvent(socket, method) { + return new Promise((resolveEvent, rejectEvent) => { + const onMessage = (raw) => { + try { + const message = JSON.parse(raw.toString()); + if (message.type !== 'event' || message.method !== method) return; + socket.off('message', onMessage); + resolveEvent(message); + } catch (error) { + socket.off('message', onMessage); + rejectEvent(error); + } + }; + socket.on('message', onMessage); + }); +} + +async function callBridge(socket, id, method, params) { + const response = nextBridgeResponse(socket, id); + socket.send(JSON.stringify({ id, type: 'request', method, params })); + return await response; +} + +const browserErrors = []; +let context; +try { + context = await chromium.launchPersistentContext(userDataDir, { + executablePath, + headless: true, + viewport: { width: 1280, height: 760 }, + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + '--host-resolver-rules=MAP yakit-insecure.test 127.0.0.1', + '--no-first-run', + '--no-default-browser-check', + ], + }); + context.on('page', (page) => { + page.on('pageerror', (error) => browserErrors.push(`${page.url()}: ${error.message}`)); + }); + + let serviceWorker = context.serviceWorkers()[0]; + if (!serviceWorker) serviceWorker = await context.waitForEvent('serviceworker', { timeout: 15_000 }); + const extensionId = new URL(serviceWorker.url()).host; + + if (shouldEnableUserScripts) { + const extensionsPage = await context.newPage(); + await extensionsPage.goto(`chrome://extensions/?id=${extensionId}`); + const userScriptsToggle = extensionsPage.locator('#allow-user-scripts cr-toggle'); + await userScriptsToggle.waitFor({ state: 'visible', timeout: 10_000 }); + const enabled = await userScriptsToggle.evaluate((toggle) => Boolean(toggle.checked)); + if (!enabled) await userScriptsToggle.click(); + await extensionsPage.close(); + } + + const webPage = await context.newPage(); + await webPage.setViewportSize({ width: 1280, height: 760 }); + await webPage.goto(testUrl); + const tabs = await serviceWorker.evaluate(async () => await chrome.tabs.query({})); + const targetTab = tabs.find((tab) => tab.url === testUrl); + if (!targetTab?.id) throw new Error('Could not resolve the test tab ID'); + await webPage.evaluate(async () => { + await new Promise((resolveDatabase, rejectDatabase) => { + const open = indexedDB.open('yakit-e2e-auth', 1); + open.onupgradeneeded = () => open.result.createObjectStore('sessions'); + open.onerror = () => rejectDatabase(open.error); + open.onsuccess = () => { + const database = open.result; + const transaction = database.transaction('sessions', 'readwrite'); + transaction.objectStore('sessions').put({ authenticated: true }, 'account-1'); + transaction.oncomplete = () => { database.close(); resolveDatabase(); }; + transaction.onerror = () => rejectDatabase(transaction.error); + }; + }); + const cache = await caches.open('yakit-e2e-session-cache'); + await cache.put('/e2e-cached-session', new Response('cached')); + history.pushState({ source: 'e2e' }, '', '/authenticated?spa=inventory'); + window.CryptoJS = { + SHA256(value) { + return { sigBytes: 32, toString: () => `sha256:${value}` }; + }, + }; + window.__yakitObserverOriginals = { + fetch: window.fetch, + xhrOpen: XMLHttpRequest.prototype.open, + xhrSend: XMLHttpRequest.prototype.send, + webSocket: window.WebSocket, + digest: Object.getPrototypeOf(crypto.subtle).digest, + cryptoJsSha256: window.CryptoJS.SHA256, + }; + }); + + const popup = await context.newPage(); + await popup.setViewportSize({ width: 390, height: 560 }); + await popup.goto(`chrome-extension://${extensionId}/popup.html`); + await popup.locator('.popup-shell').waitFor(); + await popup.getByText('Authenticated Security Console', { exact: true }).waitFor(); + const popupBrandsLoaded = await popup.locator('.yak-mark, .yakit-mark').evaluateAll((images) => images.every((image) => image instanceof HTMLImageElement && image.complete && image.naturalWidth > 0)); + if (!popupBrandsLoaded) throw new Error('Popup brand assets did not load'); + await popup.screenshot({ path: resolve(artifacts, 'popup.png') }); + + const options = await context.newPage(); + await options.setViewportSize({ width: 1440, height: 900 }); + await options.goto(`chrome-extension://${extensionId}/options.html?tabId=${targetTab.id}#overview`); + await options.locator('.app-shell').waitFor(); + if (await options.locator('.target-tab-select').inputValue() !== String(targetTab.id)) throw new Error('Options did not preserve the explicit target tab'); + const lateOpenedPage = await context.newPage(); + await lateOpenedPage.goto(testUrl); + await lateOpenedPage.evaluate(() => { document.title = 'Opened after Options'; }); + const lateOpenedOption = options.locator('.target-tab-select option', { hasText: 'Opened after Options' }); + await lateOpenedOption.waitFor({ state: 'attached' }); + if (await options.locator('.target-tab-select').inputValue() !== String(targetTab.id)) throw new Error('A newly opened tab replaced the explicit Options target'); + await lateOpenedPage.close(); + await lateOpenedOption.waitFor({ state: 'detached' }); + await options.waitForTimeout(300); + await options.screenshot({ path: resolve(artifacts, 'options-overview.png') }); + await options.getByRole('button', { name: '引擎连接' }).click(); + await options.getByText('连接本机 Yakit', { exact: true }).waitFor(); + await options.waitForTimeout(220); + await options.screenshot({ path: resolve(artifacts, 'options-engine-unpaired.png') }); + const unpairedEngineBounds = await options.evaluate(() => ({ clientWidth: document.documentElement.clientWidth, scrollWidth: document.documentElement.scrollWidth })); + if (unpairedEngineBounds.scrollWidth > unpairedEngineBounds.clientWidth) throw new Error(`Unpaired engine UI overflowed: ${JSON.stringify(unpairedEngineBounds)}`); + await options.getByRole('button', { name: '运行概览' }).click(); + const strictPage = await context.newPage(); + await strictPage.goto(`http://127.0.0.1:${address.port}/strict-csp`); + const strictTab = (await serviceWorker.evaluate(async () => await chrome.tabs.query({}))).find((item) => item.url?.includes('/strict-csp')); + if (!strictTab?.id) throw new Error('Could not resolve strict CSP tab'); + const strictEval = await options.evaluate(async (tabId) => await chrome.runtime.sendMessage({ + action: 'context.eval', payload: { tabId, mode: 'expression', code: 'document.title', timeoutMs: 2_000 }, + }), strictTab.id); + if (shouldEnableUserScripts) { + if (!strictEval?.ok || strictEval.data?.value !== 'Strict CSP Page') throw new Error(`Strict CSP page execution failed: ${JSON.stringify(strictEval)}`); + } else if (strictEval?.ok) { + throw new Error(`Injected fallback unexpectedly bypassed strict page CSP: ${JSON.stringify(strictEval)}`); + } + await strictPage.close(); + + const closingPage = await context.newPage(); + await closingPage.goto(testUrl); + const closingTab = (await serviceWorker.evaluate(async () => await chrome.tabs.query({}))).find((item) => item.url === testUrl && item.id !== targetTab.id); + if (!closingTab?.id) throw new Error('Could not resolve closing Eval tab'); + const closingEvalPromise = options.evaluate(async (tabId) => await chrome.runtime.sendMessage({ + action: 'context.eval', + payload: { tabId, mode: 'expression', code: 'new Promise((resolve) => setTimeout(() => resolve(document.title), 2000))', timeoutMs: 5_000 }, + }), closingTab.id); + await options.waitForTimeout(50); + await closingPage.close(); + const closingEval = await closingEvalPromise; + if (closingEval?.ok || !['target_unavailable', 'request_failed'].includes(closingEval?.errorCode)) throw new Error(`Closing tab Eval did not fail closed: ${JSON.stringify(closingEval)}`); + + await webPage.evaluate(() => { + postMessage({ action: 'grant.create', payload: { scopes: ['browser.page.eval.program'] } }, '*'); + dispatchEvent(new CustomEvent('yakit:page-response:v1', { detail: JSON.stringify({ id: 'forged', ok: true, result: { value: 'forged' } }) })); + }); + const forgedState = await options.evaluate(async () => await chrome.runtime.sendMessage({ action: 'state.get' })); + if (!forgedState?.ok || forgedState.data?.activeGrant) throw new Error(`Page forged an extension grant: ${JSON.stringify(forgedState)}`); + await options.getByRole('button', { name: 'Cookie Editor' }).click(); + const testCookie = options.getByRole('button', { name: 'yakit_e2e_session' }); + await testCookie.waitFor(); + const cookieValueButton = options.getByTitle('显示 Cookie 值').first(); + if (!(await cookieValueButton.textContent()).includes('[hidden')) throw new Error('Cookie Editor exposed a value by default'); + await cookieValueButton.click(); + if (!(await options.getByTitle('隐藏 Cookie 值').first().textContent()).includes('authenticated')) throw new Error('Cookie Editor did not reveal a value on explicit click'); + await options.getByTitle('隐藏 Cookie 值').first().click(); + await testCookie.click(); + if (await options.locator('.rule-editor input').first().inputValue() !== 'yakit_e2e_session') throw new Error('Cookie Editor did not load the selected HttpOnly cookie'); + if (await options.getByText('HttpOnly', { exact: true }).count() === 0) throw new Error('Cookie Editor did not expose HttpOnly metadata'); + const cookieTransferChecks = await options.evaluate(async ({ url }) => { + const send = async (action, payload) => { + const response = await chrome.runtime.sendMessage({ action, payload }); + if (!response?.ok) throw new Error(response?.error || action); + return response.data; + }; + const redacted = await send('cookie.export', { url, format: 'set-cookie', includeValues: false }); + const sensitive = await send('cookie.export', { url, format: 'json', includeValues: true }); + const imports = []; + imports.push(await send('cookie.import', { url, format: 'json', text: JSON.stringify([{ name: 'json_import', value: 'json-value', path: '/' }]) })); + imports.push(await send('cookie.import', { url, format: 'netscape', text: '127.0.0.1\tFALSE\t/\tFALSE\t0\tnetscape_import\tnetscape-value\n' })); + imports.push(await send('cookie.import', { url, format: 'set-cookie', text: 'Set-Cookie: raw_import=raw-value; Path=/; HttpOnly; SameSite=Lax; Priority=High' })); + const listed = await send('cookie.list', { url }); + const imported = listed.filter((cookie) => ['json_import', 'netscape_import', 'raw_import'].includes(cookie.name)); + const removed = await send('cookie.removeMany', { cookies: imported.map((cookie) => ({ + url: `${cookie.secure ? 'https' : 'http'}://${cookie.domain.replace(/^\./, '')}${cookie.path}`, + name: cookie.name, storeId: cookie.storeId, partitionKey: cookie.partitionKey, + })) }); + return { redacted, sensitive, imports, imported: imported.map((cookie) => cookie.name), removed }; + }, { url: testUrl }); + if (!cookieTransferChecks.redacted.includes('[REDACTED]') || cookieTransferChecks.redacted.includes('authenticated')) throw new Error(`Cookie export was not redacted: ${cookieTransferChecks.redacted}`); + if (!cookieTransferChecks.sensitive.includes('authenticated')) throw new Error('Explicit Cookie value export omitted values'); + if (cookieTransferChecks.imported.length !== 3 || cookieTransferChecks.removed.removed !== 3 || cookieTransferChecks.imports[2].warnings.length === 0) { + throw new Error(`Cookie import/export/bulk delete failed: ${JSON.stringify(cookieTransferChecks)}`); + } + await options.screenshot({ path: resolve(artifacts, 'options-cookie-editor.png') }); + await options.getByRole('button', { name: '登录态工作区' }).click(); + await options.getByRole('tab', { name: '主世界 Eval' }).click(); + await options.screenshot({ path: resolve(artifacts, 'options-context-eval.png') }); + const protocolChecks = await options.evaluate(async () => { + const invalid = await chrome.runtime.sendMessage({ action: 'panel.update', payload: { enabled: true, unexpected: true } }); + const proxyResponse = await chrome.runtime.sendMessage({ action: 'proxy.switch', payload: { id: 'direct' } }); + const proxySettings = await chrome.proxy.settings.get({}); + await Promise.all([ + chrome.runtime.sendMessage({ action: 'panel.update', payload: { side: 'right' } }), + chrome.runtime.sendMessage({ action: 'panel.update', payload: { y: 0.46 } }), + ]); + const state = await chrome.runtime.sendMessage({ action: 'state.get' }); + return { invalid, proxyResponse, proxyMode: proxySettings.value?.mode, floatingPanel: state.data?.floatingPanel }; + }); + if (protocolChecks.invalid?.ok || !protocolChecks.invalid?.error?.includes('参数无效')) throw new Error(`Runtime schema accepted an unknown field: ${JSON.stringify(protocolChecks.invalid)}`); + if (protocolChecks.floatingPanel?.side !== 'right' || protocolChecks.floatingPanel?.y !== 0.46) throw new Error(`Concurrent state updates lost data: ${JSON.stringify(protocolChecks.floatingPanel)}`); + if (!protocolChecks.proxyResponse?.ok || protocolChecks.proxyMode !== 'direct') throw new Error(`Direct proxy mode was not applied explicitly: ${JSON.stringify(protocolChecks)}`); + const proxyRuleChecks = await options.evaluate(async ({ url }) => { + const send = async (action, payload) => { + const response = await chrome.runtime.sendMessage({ action, payload }); + if (!response?.ok) throw new Error(response?.error || action); + return response.data; + }; + const directRule = { id: 'e2e-direct-rule', name: 'E2E direct', enabled: true, patterns: ['127.0.0.1'], proxyProfileId: 'direct', priority: 200 }; + const mitmRule = { id: 'e2e-mitm-rule', name: 'E2E MITM conflict', enabled: true, patterns: ['127.0.0.1'], proxyProfileId: 'yakit-mitm', priority: 100 }; + await send('proxy.rule.save', directRule); + await send('proxy.rule.save', mitmRule); + await send('proxy.rules.settings', { defaultProfileId: 'direct', failMode: 'open' }); + const firstPreview = await send('proxy.rules.preview', { url }); + const pac = await send('proxy.rules.compile'); + const auth = await send('proxy.auth.set', { profileId: 'yakit-mitm', password: 'proxy-session-secret-818' }); + const authStatus = await send('proxy.auth.status', { profileId: 'yakit-mitm' }); + const reordered = await send('proxy.rules.reorder', { ids: ['e2e-mitm-rule', 'e2e-direct-rule'] }); + const secondPreview = await send('proxy.rules.preview', { url }); + await send('proxy.rules.reorder', { ids: ['e2e-direct-rule', 'e2e-mitm-rule'] }); + const configuration = await send('proxy.config.export'); + const imported = await send('proxy.config.import', { configuration }); + await send('proxy.rules.apply'); + return { firstPreview, secondPreview, pac, auth, authStatus, reordered: reordered.proxyRules, imported: imported.proxyRouting, configuration }; + }, { url: testUrl }); + if (!proxyRuleChecks.firstPreview.conflict || proxyRuleChecks.firstPreview.effectiveProfileId !== 'direct' || proxyRuleChecks.secondPreview.effectiveProfileId !== 'yakit-mitm') { + throw new Error(`Proxy priority/conflict preview failed: ${JSON.stringify(proxyRuleChecks)}`); + } + if (!proxyRuleChecks.pac.includes('priority=200') || !proxyRuleChecks.pac.includes('; DIRECT') || !proxyRuleChecks.auth.configured || !proxyRuleChecks.authStatus.configured) { + throw new Error(`Proxy PAC/fail-open/auth failed: ${JSON.stringify(proxyRuleChecks)}`); + } + await webPage.evaluate(async () => { + const response = await fetch('/api/session?source=proxy-rule-stats'); + if (!response.ok) throw new Error(`Proxy stats request failed: ${response.status}`); + }); + const proxyStats = await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'proxy.rules.stats' }); + if (!response?.ok) throw new Error(response?.error || 'proxy.rules.stats'); + return response.data; + }); + if (!proxyStats.some((item) => item.ruleId === 'e2e-direct-rule' && item.hits > 0)) throw new Error(`Proxy rule hit statistics were not recorded: ${JSON.stringify(proxyStats)}`); + await options.getByRole('button', { name: '代理规则' }).click(); + await options.getByText('多个出口冲突,使用最高优先级', { exact: true }).waitFor(); + await options.waitForTimeout(350); + await options.screenshot({ path: resolve(artifacts, 'options-proxy-rules.png') }); + await options.evaluate(async () => { + await chrome.runtime.sendMessage({ action: 'proxy.auth.set', payload: { profileId: 'yakit-mitm', password: '' } }); + await chrome.runtime.sendMessage({ action: 'proxy.switch', payload: { id: 'direct' } }); + }); + + const host = webPage.locator('yakit-browser-agent'); + await host.waitFor({ state: 'attached', timeout: 10_000 }); + const launcher = webPage.locator('.floating-panel__brand'); + await launcher.waitFor({ state: 'visible', timeout: 10_000 }); + const launcherImageLoaded = await launcher.locator('img').evaluate((image) => image instanceof HTMLImageElement && image.complete && image.naturalWidth > 0); + if (!launcherImageLoaded) throw new Error('Floating Yak asset did not load'); + await launcher.click(); + await webPage.locator('.floating-panel.is-expanded').waitFor(); + await webPage.waitForTimeout(250); + const extensionTabs = await serviceWorker.evaluate(async () => await chrome.tabs.query({})); + const optionsTab = extensionTabs.find((tab) => tab.url?.includes('/options.html')); + const floatingFrame = webPage.frames().find((frame) => frame.url().includes('/floating.html')); + if (!optionsTab?.id || !floatingFrame) throw new Error('Could not resolve floating frame sender boundary'); + await floatingFrame.locator('.floating-panel--embedded .floating-panel__body').waitFor({ state: 'visible', timeout: 10_000 }); + await floatingFrame.getByText('快速切换', { exact: true }).waitFor({ state: 'visible' }); + const floatingBrandLoaded = await floatingFrame.locator('.floating-panel__brand img').evaluate((image) => ( + image instanceof HTMLImageElement && image.complete && image.naturalWidth > 0 + )); + if (!floatingBrandLoaded) throw new Error('Expanded floating panel Yak asset did not load'); + await floatingFrame.evaluate(async () => { + await new Promise((resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(resolveFrame))); + }); + await webPage.evaluate(async () => { + await new Promise((resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(resolveFrame))); + }); + const crossTabGrant = await floatingFrame.evaluate(async (tabId) => await chrome.runtime.sendMessage({ + action: 'grant.create', + payload: { + targets: [{ tabId, frameId: 0 }], + scopes: ['browser.tabs.read', 'browser.dom.read'], + durationMinutes: 5, + }, + }), optionsTab.id); + if (crossTabGrant?.ok || !crossTabGrant?.error?.includes('当前标签页')) throw new Error(`Floating frame crossed its sender tab boundary: ${JSON.stringify(crossTabGrant)}`); + const rightMetrics = await webPage.locator('.floating-panel').evaluate((panel) => { + const rect = panel.getBoundingClientRect(); + return { width: rect.width, left: rect.left, right: rect.right, viewportWidth: innerWidth }; + }); + if (rightMetrics.width !== 326 || rightMetrics.left < 0 || rightMetrics.right > rightMetrics.viewportWidth) { + throw new Error(`Right floating panel is clipped: ${JSON.stringify(rightMetrics)}`); + } + await webPage.screenshot({ path: resolve(artifacts, 'floating-panel-right.png') }); + + const headerBox = await webPage.locator('.floating-panel__header').boundingBox(); + if (!headerBox) throw new Error('Floating panel header has no layout box'); + await webPage.mouse.move(headerBox.x + 20, headerBox.y + 20); + await webPage.mouse.down(); + await webPage.mouse.move(22, 300, { steps: 8 }); + await webPage.mouse.up(); + await webPage.waitForTimeout(250); + const snappedLeft = await webPage.locator('.floating-panel').evaluate((panel) => panel.classList.contains('floating-panel--left')); + if (!snappedLeft) throw new Error('Floating panel did not snap to the left edge'); + await webPage.screenshot({ path: resolve(artifacts, 'floating-panel-left.png') }); + + await options.evaluate(async ({ endpoint, tabId }) => { + const send = async (action, payload) => { + const response = await chrome.runtime.sendMessage({ action, payload }); + if (!response?.ok) throw new Error(response?.error || action); + return response.data; + }; + const state = await send('state.get'); + await send('bridge.config.save', { + transport: 'websocket', + nativeHost: 'com.yaklang.browser_agent', + endpoint, + autoConnect: false, + installationId: state.bridge.installationId, + }); + await send('bridge.pair'); + for (let attempt = 0; attempt < 50; attempt += 1) { + const [nextState, bridgeStatus] = await Promise.all([send('state.get'), send('bridge.status')]); + if (nextState.bridge.pairedEngine && bridgeStatus.state === 'connected') break; + await new Promise((resolve) => setTimeout(resolve, 50)); + if (attempt === 49) throw new Error(`Bridge v3 did not connect: ${JSON.stringify({ nextState, bridgeStatus })}`); + } + await send('grant.create', { + targets: [{ tabId, frameId: 0 }], + scopes: ['browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read'], + durationMinutes: 5, + }); + }, { endpoint: bridgeEndpoint, tabId: targetTab.id }); + const { socket: bridgeSocket, hello } = await bridgeConnection; + if (hello.type !== 'auth' || hello.client !== 'yakit-browser-extension' || hello.protocolVersion !== 3 || !hello.installationId || !hello.signature || !hello.capabilities?.includes('browser.eval')) { + throw new Error(`Unexpected Bridge hello: ${JSON.stringify(hello)}`); + } + await options.getByRole('button', { name: '引擎连接' }).click(); + await options.getByText('浏览器已安全配对', { exact: true }).waitFor(); + await options.waitForTimeout(220); + await options.screenshot({ path: resolve(artifacts, 'options-engine-paired.png') }); + await options.setViewportSize({ width: 390, height: 844 }); + await options.waitForTimeout(150); + const narrowEngineBounds = await options.evaluate(() => ({ clientWidth: document.documentElement.clientWidth, scrollWidth: document.documentElement.scrollWidth })); + if (narrowEngineBounds.scrollWidth > narrowEngineBounds.clientWidth) throw new Error(`Narrow engine UI overflowed: ${JSON.stringify(narrowEngineBounds)}`); + await options.screenshot({ path: resolve(artifacts, 'options-engine-paired-narrow.png') }); + await options.setViewportSize({ width: 1440, height: 900 }); + await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'agent.pause' }); + if (!response?.ok || response.data?.state !== 'paused') throw new Error(response?.error || 'agent.pause'); + }); + const pausedCall = await callBridge(bridgeSocket, 'verify-agent-paused', 'browser.tabs', {}); + if (pausedCall.error?.code !== 'agent_paused') throw new Error(`Paused Agent accepted a capability: ${JSON.stringify(pausedCall)}`); + await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'agent.resume' }); + if (!response?.ok || response.data?.state !== 'running') throw new Error(response?.error || 'agent.resume'); + }); + const deniedBridgeEval = await callBridge(bridgeSocket, 'verify-read-denied', 'browser.eval', { + tabId: targetTab.id, + mode: 'expression', + code: 'document.title', + }); + if (!deniedBridgeEval.error?.message?.includes('browser.page.eval')) { + throw new Error(`Read grant unexpectedly allowed browser.eval: ${JSON.stringify(deniedBridgeEval)}`); + } + await options.evaluate(async ({ tabId }) => { + const response = await chrome.runtime.sendMessage({ + action: 'grant.create', + payload: { + targets: [{ tabId, frameId: 0 }], + scopes: [ + 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', + 'browser.dom.write', + 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', + 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', + 'browser.observation.read', 'browser.observation.control', + 'browser.proxy.read', 'browser.proxy.write', + ], + durationMinutes: 5, + }, + }); + if (!response?.ok) throw new Error(response?.error || 'grant.create'); + }, { tabId: targetTab.id }); + const frameInventory = await callBridge(bridgeSocket, 'verify-frame-inventory', 'browser.frames', { tabId: targetTab.id }); + const sameOriginFrame = frameInventory.result?.find((frame) => frame.url?.includes('/frame-same')); + const crossOriginFrame = frameInventory.result?.find((frame) => frame.url?.includes('/frame-cross')); + if (frameInventory.error || !sameOriginFrame?.accessible || !sameOriginFrame.sameOrigin || !crossOriginFrame?.accessible || crossOriginFrame.sameOrigin) { + throw new Error(`Frame inventory did not distinguish same/cross-origin frames: ${JSON.stringify(frameInventory)}`); + } + const deniedCrossFrame = await callBridge(bridgeSocket, 'verify-cross-frame-denied', 'browser.context', { + tabId: targetTab.id, + frameId: crossOriginFrame.frameId, + }); + if (deniedCrossFrame.error?.code !== 'target_denied') { + throw new Error(`Unselected cross-origin frame was readable: ${JSON.stringify(deniedCrossFrame)}`); + } + await options.evaluate(async ({ tabId, frameIds }) => { + const response = await chrome.runtime.sendMessage({ + action: 'grant.create', + payload: { + targets: [{ tabId, frameId: 0 }, ...frameIds.map((frameId) => ({ tabId, frameId }))], + scopes: [ + 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', + 'browser.dom.write', + 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', + 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', + 'browser.observation.read', 'browser.observation.control', + 'browser.proxy.read', 'browser.proxy.write', + ], + durationMinutes: 5, + }, + }); + if (!response?.ok) throw new Error(response?.error || 'grant.create frames'); + }, { tabId: targetTab.id, frameIds: [sameOriginFrame.frameId, crossOriginFrame.frameId] }); + const crossFrameContext = await callBridge(bridgeSocket, 'verify-cross-frame-context', 'browser.context', { + tabId: targetTab.id, + frameId: crossOriginFrame.frameId, + includeDom: true, + }); + if (crossFrameContext.error || crossFrameContext.result?.document?.title !== 'Cross Origin Account Frame') { + throw new Error(`Explicitly granted cross-origin frame was not readable: ${JSON.stringify(crossFrameContext)}`); + } + const sharedTabs = await callBridge(bridgeSocket, 'verify-deduplicated-tabs', 'browser.tabs', {}); + if (sharedTabs.error || sharedTabs.result?.length !== 1 || sharedTabs.result[0]?.id !== targetTab.id) { + throw new Error(`browser.tabs did not de-duplicate multi-frame grants: ${JSON.stringify(sharedTabs)}`); + } + const deniedProgramEval = await callBridge(bridgeSocket, 'verify-program-eval-denied', 'browser.eval', { + tabId: targetTab.id, + mode: 'program', + code: 'const value = 41; value + 1', + }); + if (!deniedProgramEval.error?.message?.includes('browser.page.eval.program')) { + throw new Error(`Expression-only grant allowed program Eval: ${JSON.stringify(deniedProgramEval)}`); + } + const observationStart = await callBridge(bridgeSocket, 'verify-observation-start', 'browser.observe.start', { + tabId: targetTab.id, + captureValues: false, + maxEntries: 100, + }); + if (observationStart.error || observationStart.result?.active !== true) { + throw new Error(`Page observation did not start: ${JSON.stringify(observationStart)}`); + } + await webPage.evaluate(async (socketPort) => { + const form = document.querySelector('form'); + form.addEventListener('submit', (event) => event.preventDefault(), { once: true }); + form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); + await fetch('/api/session?source=observer-fetch', { method: 'POST', body: 'observer-fetch-secret-171' }); + await new Promise((resolveRequest, rejectRequest) => { + const request = new XMLHttpRequest(); + request.open('POST', '/api/session?source=observer-xhr'); + request.onload = resolveRequest; + request.onerror = rejectRequest; + request.send('observer-xhr-secret-272'); + }); + await crypto.subtle.digest('SHA-256', new TextEncoder().encode('observer-webcrypto-secret-373')); + window.CryptoJS.SHA256('observer-cryptojs-secret-474'); + await new Promise((resolveSocket, rejectSocket) => { + const socket = new WebSocket(`ws://127.0.0.1:${socketPort}/page-socket`); + socket.onopen = () => socket.send('observer-websocket-secret-575'); + socket.onmessage = () => socket.close(); + socket.onclose = resolveSocket; + socket.onerror = rejectSocket; + }); + }, address.port); + const observationList = await callBridge(bridgeSocket, 'verify-observation-list', 'browser.observe.list', { tabId: targetTab.id, limit: 100 }); + const observedKinds = new Set(observationList.result?.map((item) => item.kind)); + for (const kind of ['fetch', 'xhr', 'form', 'websocket', 'webcrypto', 'cryptojs']) { + if (!observedKinds.has(kind)) throw new Error(`Page observation missed ${kind}: ${JSON.stringify(observationList)}`); + } + const redactedObservations = JSON.stringify(observationList.result); + for (const secret of ['observer-fetch-secret-171', 'observer-xhr-secret-272', 'observer-webcrypto-secret-373', 'observer-cryptojs-secret-474', 'observer-websocket-secret-575']) { + if (redactedObservations.includes(secret)) throw new Error(`Metadata-only observation leaked a value: ${secret}`); + } + const deniedSensitiveObservation = await callBridge(bridgeSocket, 'verify-observation-sensitive-denied', 'browser.observe.start', { + tabId: targetTab.id, + captureValues: true, + }); + if (!deniedSensitiveObservation.error?.message?.includes('browser.observation.sensitive.read')) { + throw new Error(`Observation value capture did not require its sensitive scope: ${JSON.stringify(deniedSensitiveObservation)}`); + } + await callBridge(bridgeSocket, 'verify-observation-stop-metadata', 'browser.observe.stop', { tabId: targetTab.id }); + await options.evaluate(async ({ tabId, frameIds }) => { + const response = await chrome.runtime.sendMessage({ + action: 'grant.create', + payload: { + targets: [{ tabId, frameId: 0 }, ...frameIds.map((frameId) => ({ tabId, frameId }))], + scopes: [ + 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', + 'browser.dom.write', 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.page.eval.program', 'browser.human.takeover', + 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', + 'browser.observation.read', 'browser.observation.control', 'browser.observation.sensitive.read', + 'browser.proxy.read', 'browser.proxy.write', + ], + durationMinutes: 5, + }, + }); + if (!response?.ok) throw new Error(response?.error || 'grant.create observation sensitive'); + }, { tabId: targetTab.id, frameIds: [sameOriginFrame.frameId, crossOriginFrame.frameId] }); + const sensitiveObservationStart = await callBridge(bridgeSocket, 'verify-observation-sensitive-start', 'browser.observe.start', { + tabId: targetTab.id, + captureValues: true, + }); + if (sensitiveObservationStart.error) throw new Error(`Sensitive page observation did not start: ${JSON.stringify(sensitiveObservationStart)}`); + const programEval = await callBridge(bridgeSocket, 'verify-program-eval', 'browser.eval', { + tabId: targetTab.id, + mode: 'program', + code: 'const programValue = 41; return programValue + 1', + }); + if (programEval.error || programEval.result?.value !== 42) throw new Error(`Program Eval scope did not execute: ${JSON.stringify(programEval)}`); + await webPage.evaluate(() => window.CryptoJS.SHA256('observer-sensitive-preview-686')); + const sensitiveObservations = await callBridge(bridgeSocket, 'verify-observation-sensitive-list', 'browser.observe.list', { tabId: targetTab.id }); + if (!JSON.stringify(sensitiveObservations.result).includes('observer-sensitive-preview-686')) { + throw new Error(`Explicit observation value capture did not return its bounded preview: ${JSON.stringify(sensitiveObservations)}`); + } + await callBridge(bridgeSocket, 'verify-observation-stop-sensitive', 'browser.observe.stop', { tabId: targetTab.id }); + const observerRestored = await webPage.evaluate(() => ({ + fetch: window.fetch === window.__yakitObserverOriginals.fetch, + xhrOpen: XMLHttpRequest.prototype.open === window.__yakitObserverOriginals.xhrOpen, + xhrSend: XMLHttpRequest.prototype.send === window.__yakitObserverOriginals.xhrSend, + webSocket: window.WebSocket === window.__yakitObserverOriginals.webSocket, + digest: Object.getPrototypeOf(crypto.subtle).digest === window.__yakitObserverOriginals.digest, + cryptoJs: window.CryptoJS.SHA256 === window.__yakitObserverOriginals.cryptoJsSha256, + })); + if (Object.values(observerRestored).some((value) => !value)) throw new Error(`Page APIs were not restored after observation: ${JSON.stringify(observerRestored)}`); + + const insecurePage = await context.newPage(); + await insecurePage.goto(insecureTestUrl); + const insecureTab = (await serviceWorker.evaluate(async () => await chrome.tabs.query({}))) + .find((tab) => tab.url === insecureTestUrl); + if (!insecureTab?.id) throw new Error('Could not resolve the insecure HTTP test tab'); + const insecureEnvironment = await insecurePage.evaluate(() => ({ + isSecureContext, + randomUUID: typeof crypto.randomUUID, + getRandomValues: typeof crypto.getRandomValues, + cacheStorage: typeof globalThis.caches, + indexedDB: typeof globalThis.indexedDB, + })); + if (insecureEnvironment.isSecureContext || insecureEnvironment.randomUUID !== 'undefined' + || insecureEnvironment.getRandomValues !== 'function' || insecureEnvironment.cacheStorage !== 'undefined') { + throw new Error(`Insecure HTTP test did not reproduce the required Crypto environment: ${JSON.stringify(insecureEnvironment)}`); + } + const insecureStorageFixture = await insecurePage.evaluate(async () => { + localStorage.setItem('yakit-insecure-local', 'local-session-value'); + sessionStorage.setItem('yakit-insecure-session', 'tab-session-value'); + if (!globalThis.indexedDB || typeof globalThis.indexedDB.databases !== 'function') return { indexedDB: false }; + await new Promise((resolveDatabase, rejectDatabase) => { + const open = globalThis.indexedDB.open('yakit-insecure-auth', 1); + open.onupgradeneeded = () => open.result.createObjectStore('sessions'); + open.onerror = () => rejectDatabase(open.error); + open.onsuccess = () => { + const database = open.result; + const transaction = database.transaction('sessions', 'readwrite'); + transaction.objectStore('sessions').put({ authenticated: true }, 'insecure-account'); + transaction.oncomplete = () => { database.close(); resolveDatabase(); }; + transaction.onerror = () => rejectDatabase(transaction.error); + }; + }); + return { indexedDB: true }; + }); + const insecureContext = await options.evaluate(async (tabId) => { + return await chrome.runtime.sendMessage({ + action: 'context.capture', + payload: { tabId, includeDom: false, includeStorage: true, includeCookies: false }, + }); + }, insecureTab.id); + const insecureDocument = insecureContext?.data?.document; + const insecureIndexedDatabase = insecureDocument?.storageInventory?.indexedDB?.databases + ?.find((database) => database.name === 'yakit-insecure-auth'); + if (!insecureContext?.ok || !insecureContext.data?.captureId + || insecureDocument?.localStorage?.supported !== true + || insecureDocument.localStorage.entries.find((entry) => entry.key === 'yakit-insecure-local')?.value !== 'local-session-value' + || insecureDocument?.sessionStorage?.supported !== true + || insecureDocument.sessionStorage.entries.find((entry) => entry.key === 'yakit-insecure-session')?.value !== 'tab-session-value' + || insecureDocument?.storageInventory?.cacheStorage?.supported !== false + || (insecureStorageFixture.indexedDB && !insecureIndexedDatabase?.stores?.some((store) => store.name === 'sessions' && store.sampleKeys.includes('insecure-account')))) { + throw new Error(`Insecure HTTP structured context did not degrade storage capabilities independently: ${JSON.stringify(insecureContext)}`); + } + await insecurePage.evaluate(() => { + window.CryptoJS = { + SHA256(value) { + return { sigBytes: 32, toString: () => `insecure-sha256:${value}` }; + }, + }; + window.__yakitInsecureObserverOriginals = { + fetch: window.fetch, + xhrOpen: XMLHttpRequest.prototype.open, + xhrSend: XMLHttpRequest.prototype.send, + webSocket: window.WebSocket, + cryptoJsSha256: window.CryptoJS.SHA256, + }; + }); + const insecureObservationStart = await options.evaluate(async (tabId) => { + return await chrome.runtime.sendMessage({ + action: 'observation.start', + payload: { tabId, captureValues: false, maxEntries: 100 }, + }); + }, insecureTab.id); + if (!insecureObservationStart?.ok || insecureObservationStart.data?.active !== true) { + throw new Error(`Insecure HTTP observation did not start: ${JSON.stringify(insecureObservationStart)}`); + } + const insecureOriginalResults = await insecurePage.evaluate(async (socketPort) => { + const cryptoJs = window.CryptoJS.SHA256('insecure-cryptojs-value').toString(); + const fetchResponse = await fetch('/api/session?source=insecure-fetch', { method: 'POST', body: 'insecure-fetch-value' }); + await new Promise((resolveRequest, rejectRequest) => { + const request = new XMLHttpRequest(); + request.open('POST', '/api/session?source=insecure-xhr'); + request.onload = resolveRequest; + request.onerror = rejectRequest; + request.send('insecure-xhr-value'); + }); + const webSocket = await new Promise((resolveSocket, rejectSocket) => { + const socket = new WebSocket(`ws://yakit-insecure.test:${socketPort}/page-socket`); + socket.onopen = () => socket.send('insecure-websocket-value'); + socket.onmessage = (event) => { + const value = event.data; + socket.close(); + resolveSocket(value); + }; + socket.onerror = rejectSocket; + }); + return { cryptoJs, fetchStatus: fetchResponse.status, webSocket }; + }, address.port); + if (insecureOriginalResults.cryptoJs !== 'insecure-sha256:insecure-cryptojs-value' + || insecureOriginalResults.fetchStatus !== 200 + || insecureOriginalResults.webSocket !== 'echo:insecure-websocket-value') { + throw new Error(`Observation changed insecure page behavior: ${JSON.stringify(insecureOriginalResults)}`); + } + const insecureObservations = await options.evaluate(async (tabId) => { + return await chrome.runtime.sendMessage({ action: 'observation.list', payload: { tabId, limit: 100 } }); + }, insecureTab.id); + if (!insecureObservations?.ok) throw new Error(`Could not read insecure HTTP observations: ${JSON.stringify(insecureObservations)}`); + const insecureKinds = new Set(insecureObservations.data.map((item) => item.kind)); + for (const kind of ['fetch', 'xhr', 'websocket', 'cryptojs']) { + if (!insecureKinds.has(kind)) throw new Error(`Insecure HTTP observation missed ${kind}: ${JSON.stringify(insecureObservations)}`); + } + const insecureObservationStop = await options.evaluate(async (tabId) => { + return await chrome.runtime.sendMessage({ action: 'observation.stop', payload: { tabId } }); + }, insecureTab.id); + if (!insecureObservationStop?.ok) throw new Error(`Could not stop insecure HTTP observation: ${JSON.stringify(insecureObservationStop)}`); + const insecureRestored = await insecurePage.evaluate(() => ({ + fetch: window.fetch === window.__yakitInsecureObserverOriginals.fetch, + xhrOpen: XMLHttpRequest.prototype.open === window.__yakitInsecureObserverOriginals.xhrOpen, + xhrSend: XMLHttpRequest.prototype.send === window.__yakitInsecureObserverOriginals.xhrSend, + webSocket: window.WebSocket === window.__yakitInsecureObserverOriginals.webSocket, + cryptoJs: window.CryptoJS.SHA256 === window.__yakitInsecureObserverOriginals.cryptoJsSha256, + })); + if (Object.values(insecureRestored).some((value) => !value)) { + throw new Error(`Insecure HTTP page APIs were not restored: ${JSON.stringify(insecureRestored)}`); + } + await insecurePage.close(); + + const bridgeEval = await callBridge(bridgeSocket, 'verify-control-eval', 'browser.eval', { + tabId: targetTab.id, + mode: 'expression', + timeoutMs: 5_000, + code: `Promise.resolve({ answer: 6 * 7, encrypted: window.app.crypto.encrypt('sensitive-e2e-value-239') })`, + }); + if (bridgeEval.error || bridgeEval.result?.value?.answer !== 42 || bridgeEval.result?.value?.encrypted !== 'page:sensitive-e2e-value-239') { + throw new Error(`Control grant browser.eval failed: ${JSON.stringify(bridgeEval)}`); + } + const cancelledResponsePromise = nextBridgeResponse(bridgeSocket, 'verify-cancelled-eval'); + bridgeSocket.send(JSON.stringify({ + id: 'verify-cancelled-eval', + type: 'request', + method: 'browser.eval', + params: { tabId: targetTab.id, mode: 'expression', timeoutMs: 5_000, code: `new Promise((resolve) => setTimeout(() => resolve('late'), 1000))` }, + })); + await new Promise((resolveWait) => setTimeout(resolveWait, 50)); + bridgeSocket.send(JSON.stringify({ id: 'verify-cancelled-eval', type: 'cancel' })); + const cancelledBridgeEval = await cancelledResponsePromise; + if (cancelledBridgeEval.error?.code !== 'cancelled') throw new Error(`Bridge cancellation was not enforced: ${JSON.stringify(cancelledBridgeEval)}`); + + const firstContext = await callBridge(bridgeSocket, 'verify-context-first', 'browser.context', { + tabId: targetTab.id, + includeDom: true, + includeStorage: true, + includeCookies: false, + }); + const accountNode = firstContext.result?.document?.interactive?.find((node) => node.name === 'account'); + const indexedDatabase = firstContext.result?.document?.storageInventory?.indexedDB?.databases?.find((database) => database.name === 'yakit-e2e-auth'); + if (firstContext.error || !firstContext.result?.captureId || !accountNode?.nodeId || 'html' in (firstContext.result?.document || {}) + || firstContext.result.document.bodyText.length > 20 * 1024 || firstContext.result.frames.length < 3 + || !indexedDatabase?.stores?.some((store) => store.name === 'sessions' && store.sampleKeys.includes('account-1')) + || !firstContext.result.document.storageInventory.cacheStorage.names.includes('yakit-e2e-session-cache') + || !firstContext.result.lifecycle.some((event) => event.kind === 'history')) { + throw new Error(`Structured browser context is invalid or unbounded: ${JSON.stringify(firstContext)}`); + } + const inspectedAccount = await callBridge(bridgeSocket, 'verify-node-inspect', 'browser.node.inspect', { + tabId: targetTab.id, + captureId: firstContext.result.captureId, + nodeId: accountNode.nodeId, + }); + if (inspectedAccount.error || inspectedAccount.result?.reference?.captureId !== firstContext.result.captureId || inspectedAccount.result?.attributes?.value) { + throw new Error(`Stable node inspection leaked a value or returned the wrong reference: ${JSON.stringify(inspectedAccount)}`); + } + const setNodeValue = await callBridge(bridgeSocket, 'verify-node-set-value', 'browser.node.action', { + tabId: targetTab.id, + captureId: firstContext.result.captureId, + nodeId: accountNode.nodeId, + action: 'setValue', + value: 'context-node-secret-e2e-448', + }); + if (setNodeValue.error || await webPage.locator('input[name="account"]').inputValue() !== 'context-node-secret-e2e-448') { + throw new Error(`Stable node setValue failed: ${JSON.stringify(setNodeValue)}`); + } + await webPage.evaluate(() => { + const button = document.createElement('button'); + button.id = 'context-diff-action'; + button.textContent = 'Context diff action'; + button.addEventListener('click', () => { window.__contextNodeClicked = true; }); + document.querySelector('main')?.append(button); + }); + const secondContext = await callBridge(bridgeSocket, 'verify-context-second', 'browser.context', { + tabId: targetTab.id, + includeDom: true, + }); + const addedActionNode = secondContext.result?.document?.interactive?.find((node) => node.nodeId && node.accessibleName === 'Context diff action'); + if (secondContext.error || secondContext.result?.diff?.kind !== 'changed' || !secondContext.result.diff.addedNodes?.some((node) => node.text === 'Context diff action') || !addedActionNode) { + throw new Error(`Context diff did not report the added action: ${JSON.stringify(secondContext)}`); + } + const staleNode = await callBridge(bridgeSocket, 'verify-stale-node', 'browser.node.inspect', { + tabId: targetTab.id, + captureId: firstContext.result.captureId, + nodeId: accountNode.nodeId, + }); + if (staleNode.error?.code !== 'stale_node') throw new Error(`Old context node reference remained valid: ${JSON.stringify(staleNode)}`); + const clickNode = await callBridge(bridgeSocket, 'verify-node-click', 'browser.node.action', { + tabId: targetTab.id, + captureId: secondContext.result.captureId, + nodeId: addedActionNode.nodeId, + action: 'click', + }); + await webPage.waitForTimeout(50); + if (clickNode.error || await webPage.evaluate(() => window.__contextNodeClicked) !== true) { + throw new Error(`Stable node click failed: ${JSON.stringify(clickNode)}`); + } + await webPage.evaluate(() => { + const container = document.createElement('div'); + container.id = 'context-performance-fixture'; + const fragment = document.createDocumentFragment(); + for (let index = 0; index < 10_500; index += 1) { + const span = document.createElement('span'); + span.textContent = `安全上下文-${index} `; + fragment.append(span); + } + container.append(fragment); + document.body.append(container); + }); + const boundedContext = await callBridge(bridgeSocket, 'verify-context-bounds', 'browser.context', { + tabId: targetTab.id, + includeDom: true, + }); + if (boundedContext.error || boundedContext.result?.document?.scannedElementCount !== 10_000 + || !boundedContext.result.document.limitsReached?.includes('scanned_elements') + || Buffer.byteLength(boundedContext.result.document.bodyText, 'utf8') > 20 * 1024) { + throw new Error(`Structured context did not enforce scan/text byte limits: ${JSON.stringify(boundedContext)}`); + } + await webPage.evaluate(() => document.getElementById('context-performance-fixture')?.remove()); + + const handoffResponse = await callBridge(bridgeSocket, 'verify-handoff', 'browser.handoff.request', { + tabId: targetTab.id, + reason: 'qr_code', + message: '请扫描测试二维码并确认登录', + }); + if (handoffResponse.error || handoffResponse.result?.state !== 'waiting_for_user') { + throw new Error(`Human handoff did not enter waiting state: ${JSON.stringify(handoffResponse)}`); + } + await options.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor(); + await floatingFrame.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor(); + await options.screenshot({ path: resolve(artifacts, 'options-human-handoff.png') }); + await webPage.screenshot({ path: resolve(artifacts, 'floating-panel-handoff.png') }); + const handoffPopup = await context.newPage(); + await handoffPopup.setViewportSize({ width: 390, height: 560 }); + await handoffPopup.goto(`chrome-extension://${extensionId}/popup.html`); + await handoffPopup.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor(); + await handoffPopup.screenshot({ path: resolve(artifacts, 'popup-human-handoff.png') }); + await handoffPopup.close(); + + const handoffEventPromise = nextBridgeEvent(bridgeSocket, 'browser.handoff.changed'); + await floatingFrame.getByRole('button', { name: '已完成' }).click(); + const handoffEvent = await handoffEventPromise; + if (handoffEvent.params?.id !== handoffResponse.result.id || handoffEvent.params?.state !== 'completed') { + throw new Error(`Engine did not receive completed handoff event: ${JSON.stringify(handoffEvent)}`); + } + await options.getByText('请扫描测试二维码并确认登录', { exact: true }).waitFor({ state: 'detached' }); + + await options.getByRole('button', { name: '操作记录' }).click(); + await options.getByText('browser.handoff.request', { exact: true }).first().waitFor(); + await options.waitForTimeout(300); + await options.screenshot({ path: resolve(artifacts, 'options-activity.png') }); + const auditEvents = await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'audit.list', payload: { limit: 200 } }); + if (!response?.ok) throw new Error(response?.error || 'audit.list'); + return response.data; + }); + if (JSON.stringify(auditEvents).includes('sensitive-e2e-value-239') || JSON.stringify(auditEvents).includes('context-node-secret-e2e-448')) throw new Error('Audit log leaked browser.eval or node action data'); + if (!auditEvents.some((event) => event.action === 'handoff.completed' && event.outcome === 'success')) throw new Error('Audit log is missing the completed handoff'); + const runtimeAndStorage = await options.evaluate(async () => { + const runtimeResponse = await chrome.runtime.sendMessage({ action: 'agent.runtime.get' }); + if (!runtimeResponse?.ok) throw new Error(runtimeResponse?.error || 'agent.runtime.get'); + return { + runtime: runtimeResponse.data, + local: await chrome.storage.local.get(null), + session: await chrome.storage.session.get(null), + }; + }); + if (runtimeAndStorage.runtime.state !== 'running' || !runtimeAndStorage.runtime.actions.some((action) => action.method === 'browser.handoff.request' && action.state === 'success')) { + throw new Error(`Agent runtime timeline is incomplete: ${JSON.stringify(runtimeAndStorage.runtime)}`); + } + for (const key of ['settings.proxy.v1', 'settings.user-agent.v1', 'settings.bridge.v2', 'ui.floating-panel.v1']) { + if (!(key in runtimeAndStorage.local)) throw new Error(`Split local storage key is missing: ${key}`); + } + if ('yakit-extension-state-v5' in runtimeAndStorage.local || JSON.stringify(runtimeAndStorage.local).includes('proxy-session-secret-818')) { + throw new Error('Local storage retained a legacy state blob or session-only proxy password'); + } + for (const key of ['session.browser-agent.v1', 'session.bridge.v1', 'session.agent-runtime.v1']) { + if (!(key in runtimeAndStorage.session)) throw new Error(`Split session storage key is missing: ${key}`); + } + + await options.getByRole('button', { name: '网络活动' }).click(); + await options.locator('.network-control-bar .ui-switch').nth(0).click(); + await options.locator('.network-control-bar .ui-switch').nth(1).click(); + await options.getByRole('button', { name: '开始捕获' }).click(); + await webPage.evaluate(async () => { + const response = await fetch('/api/session?source=browser-agent', { + method: 'POST', + headers: { 'content-type': 'application/json', 'x-yakit-e2e': 'request-header-value' }, + body: JSON.stringify({ marker: 'network-sensitive-e2e-771' }), + }); + if (!response.ok) throw new Error(`E2E API returned ${response.status}`); + await response.json(); + }); + const capturedNetworkRow = options.locator('.network-row').filter({ hasText: '/api/session?source=browser-agent' }).first(); + await capturedNetworkRow.waitFor(); + await capturedNetworkRow.click(); + await options.getByText(/x-yakit-e2e: request-header-value/i).waitFor(); + await options.screenshot({ path: resolve(artifacts, 'options-network-activity.png') }); + + const bridgeNetworkList = await callBridge(bridgeSocket, 'verify-network-list', 'browser.network.list', { + tabId: targetTab.id, + limit: 20, + }); + const capturedRequest = bridgeNetworkList.result?.find((record) => record.url?.includes('/api/session')); + if (bridgeNetworkList.error || !capturedRequest?.requestHeaders?.some((header) => header.name.toLowerCase() === 'x-yakit-e2e') || capturedRequest.requestBody?.data?.includes('network-sensitive-e2e-771') !== true) { + throw new Error(`Agent network capture did not include the explicitly granted request: ${JSON.stringify(bridgeNetworkList)}`); + } + + await options.getByRole('button', { name: 'Yakit' }).click(); + const fuzzerOpenMessage = await webFuzzerOpenRequest; + const fuzzerPacket = Buffer.from(fuzzerOpenMessage.params?.rawRequestBase64 || '', 'base64').toString('utf8'); + if (!fuzzerPacket.includes('POST /api/session?source=browser-agent HTTP/1.1') || !/x-yakit-e2e: request-header-value/i.test(fuzzerPacket) || !fuzzerPacket.includes('network-sensitive-e2e-771') || !/cookie: .*yakit_e2e_session=authenticated/i.test(fuzzerPacket)) { + throw new Error(`Web Fuzzer handoff did not contain the real authenticated request: ${fuzzerPacket}`); + } + await options.getByRole('button', { name: 'PoC' }).click(); + const pocMessage = await pocGenerateRequest; + const pocPacket = Buffer.from(pocMessage.params?.rawRequestBase64 || '', 'base64').toString('utf8'); + if (!pocPacket.includes('network-sensitive-e2e-771')) throw new Error(`Yak PoC generation did not receive the captured request: ${pocPacket}`); + await options.getByText('browser-e2e.yak', { exact: true }).waitFor(); + await options.getByRole('button', { name: '分析' }).click(); + const analysisMessage = await analysisPrepareRequest; + if (JSON.stringify(analysisMessage.params?.observations || []).includes('observer-sensitive-preview-686')) { + throw new Error('AI analysis payload included a sensitive observation preview'); + } + await options.getByText('AI 分析上下文', { exact: true }).waitFor(); + await options.screenshot({ path: resolve(artifacts, 'options-network-analysis.png') }); + const networkAudit = await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'audit.list', payload: { limit: 200 } }); + if (!response?.ok) throw new Error(response?.error || 'audit.list'); + return response.data; + }); + if (JSON.stringify(networkAudit).includes('network-sensitive-e2e-771') || JSON.stringify(networkAudit).includes('yakit_e2e_session=authenticated')) { + throw new Error('Audit log leaked captured network credentials or request body'); + } + await options.getByRole('button', { name: '停止' }).click(); + await options.getByRole('button', { name: '登录态工作区' }).click(); + await options.locator('.context-options input').nth(0).check(); + await options.locator('.context-options input').nth(1).check(); + await options.getByRole('button', { name: '采集页面' }).click(); + await webPage.evaluate(() => { + const button = document.createElement('button'); + button.id = 'context-ui-diff'; + button.textContent = 'UI context change'; + document.querySelector('main')?.append(button); + }); + await options.getByRole('button', { name: '刷新并比较' }).click(); + await options.getByText('发现变化', { exact: true }).first().waitFor(); + await options.screenshot({ path: resolve(artifacts, 'options-context-workspace.png') }); + const evalResponse = await options.evaluate(async ({ tabId }) => { + return await chrome.runtime.sendMessage({ + action: 'context.eval', + payload: { + tabId, + mode: 'program', + timeoutMs: 5_000, + code: `window.__yakitEvalMarker = 41; return Promise.resolve({ answer: window.__yakitEvalMarker + 1, encrypted: window.app.crypto.encrypt('ok'), host: location.hostname })`, + }, + }); + }, { tabId: targetTab.id }); + if (!evalResponse?.ok || evalResponse.data?.value?.answer !== 42 || evalResponse.data?.value?.encrypted !== 'page:ok') { + throw new Error(`Unexpected page Eval response: ${JSON.stringify(evalResponse)}`); + } + const timeoutResponse = await options.evaluate(async ({ tabId }) => { + return await chrome.runtime.sendMessage({ + action: 'context.eval', + payload: { tabId, mode: 'expression', timeoutMs: 250, code: `new Promise((resolve) => setTimeout(() => resolve('late'), 500))` }, + }); + }, { tabId: targetTab.id }); + if (timeoutResponse?.ok || !timeoutResponse?.error?.includes('250ms')) { + throw new Error(`Page Eval timeout was not enforced: ${JSON.stringify(timeoutResponse)}`); + } + + const panelMetrics = await webPage.locator('.floating-panel').evaluate((panel) => { + const rect = panel.getBoundingClientRect(); + return { width: rect.width, height: rect.height, left: rect.left, right: rect.right, viewportWidth: innerWidth, viewportHeight: innerHeight }; + }); + if (panelMetrics.left < 0 || panelMetrics.right > panelMetrics.viewportWidth || panelMetrics.height > 480 || panelMetrics.height > panelMetrics.viewportHeight) { + throw new Error(`Floating panel is outside the viewport: ${JSON.stringify(panelMetrics)}`); + } + + const mobileOptions = await context.newPage(); + await mobileOptions.setViewportSize({ width: 390, height: 844 }); + await mobileOptions.goto(`chrome-extension://${extensionId}/options.html#overview`); + await mobileOptions.locator('.app-shell').waitFor(); + await mobileOptions.waitForTimeout(300); + const mobileOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); + if (mobileOverflow > 0) throw new Error(`Options mobile layout overflows by ${mobileOverflow}px`); + await mobileOptions.screenshot({ path: resolve(artifacts, 'options-mobile.png'), fullPage: true }); + await mobileOptions.getByRole('button', { name: '网络活动' }).click(); + await mobileOptions.waitForTimeout(300); + const mobileNetworkOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); + if (mobileNetworkOverflow > 0) throw new Error(`Network mobile layout overflows by ${mobileNetworkOverflow}px`); + await mobileOptions.screenshot({ path: resolve(artifacts, 'options-network-mobile.png'), fullPage: true }); + await mobileOptions.getByRole('button', { name: '登录态工作区' }).click(); + await mobileOptions.getByRole('button', { name: '采集页面' }).click(); + await mobileOptions.locator('.context-session-strip').waitFor(); + const mobileContextOverflow = await mobileOptions.evaluate(() => document.documentElement.scrollWidth - innerWidth); + if (mobileContextOverflow > 0) throw new Error(`Context mobile layout overflows by ${mobileContextOverflow}px`); + await mobileOptions.screenshot({ path: resolve(artifacts, 'options-context-mobile.png'), fullPage: true }); + await mobileOptions.setViewportSize({ width: 320, height: 700 }); + await mobileOptions.goto(`chrome-extension://${extensionId}/options.html?tabId=${targetTab.id}#overview`); + await mobileOptions.locator('.task-command-bar').waitFor(); + const narrowOptionsLayout = await mobileOptions.evaluate(() => ({ + overflow: document.documentElement.scrollWidth - innerWidth, + wrappedNavLabels: [...document.querySelectorAll('.sidebar nav button span')].filter((label) => { + const style = getComputedStyle(label); + return label.getBoundingClientRect().height > Number.parseFloat(style.lineHeight || '20') * 1.5; + }).length, + })); + if (narrowOptionsLayout.overflow > 0 || narrowOptionsLayout.wrappedNavLabels > 0) throw new Error(`Options 320px navigation/layout failed: ${JSON.stringify(narrowOptionsLayout)}`); + await mobileOptions.screenshot({ path: resolve(artifacts, 'options-overview-320.png'), fullPage: true }); + + const narrowPage = await context.newPage(); + await narrowPage.setViewportSize({ width: 320, height: 640 }); + await narrowPage.goto(testUrl); + await narrowPage.locator('.floating-panel__brand').waitFor({ state: 'visible' }); + await narrowPage.locator('.floating-panel__brand').click(); + await narrowPage.waitForTimeout(250); + const narrowPanel = await narrowPage.locator('.floating-panel').evaluate((panel) => { + const rect = panel.getBoundingClientRect(); + return { width: rect.width, left: rect.left, right: rect.right, viewportWidth: innerWidth }; + }); + if (narrowPanel.left < 0 || narrowPanel.right > narrowPanel.viewportWidth) throw new Error(`Narrow floating panel overflows: ${JSON.stringify(narrowPanel)}`); + await narrowPage.screenshot({ path: resolve(artifacts, 'floating-panel-narrow.png') }); + + const agentCapture = await callBridge(bridgeSocket, 'verify-agent-capture-start', 'browser.network.start', { + tabId: targetTab.id, + captureHeaders: false, + captureBody: false, + }); + if (agentCapture.error || agentCapture.result?.active !== true) throw new Error(`Agent could not start a scoped capture: ${JSON.stringify(agentCapture)}`); + const captureAfterRevoke = await options.evaluate(async ({ tabId }) => { + const revoked = await chrome.runtime.sendMessage({ action: 'grant.revoke' }); + if (!revoked?.ok) throw new Error(revoked?.error || 'grant.revoke'); + const status = await chrome.runtime.sendMessage({ action: 'network.capture.status', payload: { tabId } }); + if (!status?.ok) throw new Error(status?.error || 'network.capture.status'); + return status.data; + }, { tabId: targetTab.id }); + if (captureAfterRevoke.active) throw new Error(`Agent-owned capture survived grant revocation: ${JSON.stringify(captureAfterRevoke)}`); + await options.evaluate(async ({ tabId }) => { + const response = await chrome.runtime.sendMessage({ + action: 'grant.create', + payload: { + targets: [{ tabId, frameId: 0 }], + scopes: [ + 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', + 'browser.dom.write', + 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', + 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', + 'browser.proxy.read', 'browser.proxy.write', + ], + durationMinutes: 5, + }, + }); + if (!response?.ok) throw new Error(response?.error || 'grant.create after capture revoke'); + }, { tabId: targetTab.id }); + + await webPage.reload(); + const staleDocumentEval = await callBridge(bridgeSocket, 'verify-document-boundary', 'browser.eval', { + tabId: targetTab.id, + mode: 'expression', + code: 'document.title', + }); + if (staleDocumentEval.error?.code !== 'stale_document') throw new Error(`Grant survived a same-origin document reload: ${JSON.stringify(staleDocumentEval)}`); + await options.evaluate(async ({ tabId }) => { + const response = await chrome.runtime.sendMessage({ + action: 'grant.create', + payload: { + targets: [{ tabId, frameId: 0 }], + scopes: [ + 'browser.tabs.read', 'browser.dom.read', 'browser.storage.read', 'browser.cookies.read', + 'browser.dom.write', + 'browser.tab.activate', 'browser.page.invoke', 'browser.page.eval.expression', 'browser.human.takeover', + 'browser.network.read', 'browser.network.capture', 'browser.network.sensitive.read', + 'browser.proxy.read', 'browser.proxy.write', + ], + durationMinutes: 5, + }, + }); + if (!response?.ok) throw new Error(response?.error || 'grant.create after reload'); + }, { tabId: targetTab.id }); + + await options.evaluate(async () => await chrome.runtime.sendMessage({ action: 'panel.update', payload: { enabled: false } })); + await webPage.locator('.floating-panel__brand').waitFor({ state: 'hidden' }); + await options.evaluate(async () => await chrome.runtime.sendMessage({ action: 'panel.update', payload: { enabled: true } })); + await webPage.locator('.floating-panel__brand').waitFor({ state: 'visible' }); + await webPage.evaluate(() => dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyY', altKey: true, shiftKey: true, bubbles: true }))); + await webPage.locator('.floating-panel.is-expanded').waitFor(); + await webPage.locator('.floating-panel__brand').click(); + const currentOrigin = new URL(testUrl).origin; + await options.evaluate(async (origin) => { + const response = await chrome.runtime.sendMessage({ action: 'panel.update', payload: { siteMode: 'denylist', siteOrigins: [origin] } }); + if (!response?.ok) throw new Error(response?.error || 'panel.update denylist'); + }, currentOrigin); + await webPage.locator('.floating-panel__brand').waitFor({ state: 'hidden' }); + await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'panel.update', payload: { siteMode: 'all', siteOrigins: [] } }); + if (!response?.ok) throw new Error(response?.error || 'panel.update all'); + }); + await webPage.locator('.floating-panel__brand').waitFor({ state: 'visible' }); + const diagnostics = await options.evaluate(async () => { + const response = await chrome.runtime.sendMessage({ action: 'diagnostics.export' }); + if (!response?.ok) throw new Error(response?.error || 'diagnostics.export'); + return response.data; + }); + const diagnosticsText = JSON.stringify(diagnostics); + if (diagnosticsText.includes('sensitive-e2e-value-239') || diagnosticsText.includes('context-node-secret-e2e-448') || diagnosticsText.includes(testUrl)) { + throw new Error('Diagnostics export leaked page, Eval, node or URL values'); + } + if (diagnostics.metrics.serviceWorkerStarts < 1 || diagnostics.metrics.heartbeatSamples < 1 || !diagnostics.metrics.capabilities['browser.context']) { + throw new Error(`Diagnostics metrics are incomplete: ${JSON.stringify(diagnostics.metrics)}`); + } + const crossOriginUrl = testUrl.replace('127.0.0.1', 'localhost'); + await webPage.goto(crossOriginUrl); + const staleOriginEval = await callBridge(bridgeSocket, 'verify-origin-boundary', 'browser.eval', { + tabId: targetTab.id, + mode: 'expression', + code: 'document.title', + }); + if (!staleOriginEval.error?.message?.includes('跨来源')) throw new Error(`Grant survived a cross-origin navigation: ${JSON.stringify(staleOriginEval)}`); + const beforeRestart = await options.evaluate(async () => { + const [state, runtime] = await Promise.all([ + chrome.runtime.sendMessage({ action: 'state.get' }), + chrome.runtime.sendMessage({ action: 'agent.runtime.get' }), + ]); + return { grantId: state.data?.activeGrant?.id, taskId: state.data?.activeGrant?.taskId, runtime: runtime.data }; + }); + const cdp = await context.newCDPSession(options); + const versionPromise = new Promise((resolveVersion, rejectVersion) => { + const timer = setTimeout(() => rejectVersion(new Error('Could not resolve extension Service Worker version')), 5_000); + cdp.on('ServiceWorker.workerVersionUpdated', ({ versions }) => { + const version = versions.find((item) => item.scriptURL.startsWith(`chrome-extension://${extensionId}/`) && item.runningStatus === 'running'); + if (!version) return; + clearTimeout(timer); + resolveVersion(version); + }); + }); + await cdp.send('ServiceWorker.enable'); + const runningVersion = await versionPromise; + await cdp.send('ServiceWorker.stopWorker', { versionId: runningVersion.versionId }); + const afterRestart = await options.evaluate(async () => { + const state = await chrome.runtime.sendMessage({ action: 'state.get' }); + const runtime = await chrome.runtime.sendMessage({ action: 'agent.runtime.get' }); + if (!state?.ok || !runtime?.ok) throw new Error(state?.error || runtime?.error || 'Service Worker restart state'); + return { grantId: state.data?.activeGrant?.id, taskId: state.data?.activeGrant?.taskId, runtime: runtime.data }; + }); + await cdp.detach(); + if (!beforeRestart.grantId || afterRestart.grantId !== beforeRestart.grantId || afterRestart.taskId !== beforeRestart.taskId || afterRestart.runtime.grantId !== beforeRestart.runtime.grantId) { + throw new Error(`Service Worker restart lost grant/task runtime: ${JSON.stringify({ beforeRestart, afterRestart })}`); + } + const unpairedIdentity = await options.evaluate(async () => { + const before = await chrome.runtime.sendMessage({ action: 'state.get' }); + const unpaired = await chrome.runtime.sendMessage({ action: 'bridge.unpair' }); + const after = await chrome.runtime.sendMessage({ action: 'state.get' }); + if (!before?.ok || !unpaired?.ok || !after?.ok) throw new Error(before?.error || unpaired?.error || after?.error || 'bridge.unpair'); + return { + beforeInstallationId: before.data.bridge.installationId, + afterInstallationId: after.data.bridge.installationId, + pairedEngine: after.data.bridge.pairedEngine, + autoConnect: after.data.bridge.autoConnect, + }; + }); + if (!unpairedIdentity.beforeInstallationId + || unpairedIdentity.afterInstallationId !== unpairedIdentity.beforeInstallationId + || unpairedIdentity.pairedEngine !== undefined + || unpairedIdentity.autoConnect !== false) { + throw new Error(`Local unpair changed the stable browser installation identity: ${JSON.stringify(unpairedIdentity)}`); + } + if (browserErrors.length > 0) throw new Error(`Browser page errors:\n${browserErrors.join('\n')}`); + + console.log(JSON.stringify({ extensionId, testUrl, evalResult: evalResponse.data, timeoutError: timeoutResponse.error, bridgeEval: bridgeEval.result, cancelledBridgeError: cancelledBridgeEval.error, handoffEvent, auditEventCount: networkAudit.length, capturedNetworkUrl: capturedRequest.url, fuzzerPageId: 'e2e-fuzzer-page', protocolChecks, staleDocumentError: staleDocumentEval.error, staleOriginError: staleOriginEval.error?.message, serviceWorkerRestart: { beforeRestart, afterRestart }, unpairedIdentity, rightMetrics, panelMetrics, narrowPanel, artifacts }, null, 2)); +} finally { + await context?.close(); + server.close(); + for (const client of bridgeServer.clients) client.terminate(); + for (const client of pairingServer.clients) client.terminate(); + bridgeServer.close(); + pairingServer.close(); + bridgeHTTPServer.close(); + await rm(userDataDir, { recursive: true, force: true }); +} diff --git a/src/app/background/index.ts b/src/app/background/index.ts new file mode 100644 index 0000000..7277b8d --- /dev/null +++ b/src/app/background/index.ts @@ -0,0 +1,603 @@ +import { browser, type Browser } from 'wxt/browser'; +import { + clearNetworkRequests, exportNetworkRequest, listNetworkRequests, networkCaptureStatus, + startNetworkCapture, stopNetworkCapture, stopNetworkCapturesForGrant, +} from '@/features/network-capture/service'; +import { capturedRequestEnginePayload } from '@/features/network-capture/workflows'; +import { + clearPageObservations, listPageObservations, pageObservationStatus, startPageObservation, + stopPageObservation, stopPageObservationsForGrant, +} from '@/features/page-observation/service'; +import type { ExtensionRequest, ExtensionResponse } from '@/types/messages'; +import { parseExtensionRequest } from '@/protocol/extension'; +import type { + BridgeGrantTarget, BrowserRequestAnalysisBundle, BrowserTarget, YakPocGenerateResult, YakitFuzzerOpenResult, +} from '@/types/models'; +import { engineBridge } from '@/features/engine-bridge/service'; +import { getFrameInventory } from '@/features/page-context/frames'; +import { getActiveTab, getTab, resolveDocumentTarget } from '@/platform/browser/targets'; +import { + actOnPageNode, capturePageContext, evalInPage, inspectPageNode, invokePageFunction, +} from '@/features/page-context/service'; +import { listCookies, removeCookie, setCookie } from '@/features/cookies/service'; +import { exportCookies, importCookies } from '@/features/cookies/transfer'; +import { + applyProxyRules, clearProxyRuleStats, compileProxyRules, getProxyRuleStats, hasProxyAuthPassword, + previewProxyRules, setProxyAuthPassword, switchProxy, +} from '@/features/proxy/service'; +import { getState, updateState } from '@/platform/storage/state'; +import { applyUserAgentRules } from '@/features/identity/user-agent'; +import { errorCode, ExtensionError } from '@/shared/errors'; +import { appendAuditEvent, clearAuditEvents, listAuditEvents } from '@/features/diagnostics/audit'; +import { + clearAgentActions, getAgentRuntime, setAgentRuntimeState, startAgentRuntime, +} from '@/features/agent-runtime/service'; +import { + applyPolicyToBridge, applyPolicyToState, assertGrantPolicy, getEnterprisePolicy, +} from '@/platform/policy/managed'; +import { createDiagnosticsBundle } from '@/features/diagnostics/export'; +import { getRuntimeMetrics, recordServiceWorkerStart, resetRuntimeMetrics } from '@/features/diagnostics/metrics'; + +function ok(data?: T): ExtensionResponse { + return { ok: true, data }; +} + +function fail(error: unknown): ExtensionResponse { + return { ok: false, error: error instanceof Error ? error.message : String(error), errorCode: errorCode(error) }; +} + +function isFloatingSender(sender: Browser.runtime.MessageSender): boolean { + try { + const parsed = new URL(sender.url || ''); + return parsed.origin === new URL(browser.runtime.getURL('/')).origin && parsed.pathname === '/floating.html'; + } catch { + return false; + } +} + +function senderBoundTabId(sender: Browser.runtime.MessageSender): number | undefined { + const extensionOrigin = new URL(browser.runtime.getURL('/')).origin; + const senderUrl = sender.url || ''; + try { + const parsed = new URL(senderUrl); + if (parsed.origin === extensionOrigin && parsed.pathname !== '/floating.html') return undefined; + } catch { + // Non-URL senders remain bound to their browser tab below. + } + return sender.tab?.id; +} + +function targetTabId(requested: number | undefined, sender: Browser.runtime.MessageSender): number | undefined { + const senderTabId = senderBoundTabId(sender); + if (senderTabId && requested && senderTabId !== requested) { + throw new Error('页面内请求不能操作其他标签页'); + } + return senderTabId || requested; +} + +async function requestTarget( + input: { tabId?: number; frameId?: number; documentId?: string }, + sender: Browser.runtime.MessageSender, +): Promise { + const boundTabId = senderBoundTabId(sender); + if (boundTabId && input.tabId && boundTabId !== input.tabId) { + throw new ExtensionError('target_denied', '页面内请求不能操作其他标签页'); + } + if (boundTabId && !isFloatingSender(sender)) { + const frameId = sender.frameId ?? 0; + if (input.frameId !== undefined && input.frameId !== frameId) throw new ExtensionError('target_denied', '页面内请求不能操作其他 frame'); + if (input.documentId && sender.documentId && input.documentId !== sender.documentId) { + throw new ExtensionError('stale_document', '目标页面已经刷新或导航,请重新选择'); + } + return { tabId: boundTabId, frameId, documentId: sender.documentId }; + } + const tabId = boundTabId || input.tabId; + if (!tabId) return undefined; + return resolveDocumentTarget({ tabId, frameId: input.frameId ?? 0, documentId: input.documentId }); +} + +async function requiredRequestTarget( + input: { tabId?: number; frameId?: number; documentId?: string }, + sender: Browser.runtime.MessageSender, +): Promise { + const target = await requestTarget(input, sender); + if (!target) throw new ExtensionError('target_unavailable', '请选择一个可访问的 HTTP(S) 标签页'); + return target; +} + +function originOf(url: string): string { + const parsed = new URL(url); + if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error('只能授权 HTTP(S) 标签页'); + return parsed.origin; +} + +async function createGrantTargets(inputs: Array<{ tabId: number; frameId: number }>): Promise { + const unique = [...new Map(inputs.map((target) => [`${target.tabId}:${target.frameId}`, target])).values()]; + const tabIds = [...new Set(unique.map((target) => target.tabId))]; + const inventories = new Map(await Promise.all(tabIds.map(async (tabId) => [tabId, await getFrameInventory(tabId)] as const))); + return Promise.all(unique.map(async (input) => { + const tab = await getTab(input.tabId); + const frame = inventories.get(input.tabId)?.find((item) => item.frameId === input.frameId); + if (!frame?.accessible || !frame.documentId || !frame.origin) { + throw new ExtensionError('target_unavailable', `Frame ${input.frameId} 当前不可访问,不能加入共享会话`); + } + originOf(`${frame.origin}/`); + return { + tabId: input.tabId, + frameId: frame.frameId, + documentId: frame.documentId, + origin: frame.origin, + grantedUrl: frame.url, + title: frame.isTop ? tab.title : `${tab.title} · ${frame.title || frame.name || `Frame ${frame.frameId}`}`, + }; + })); +} + +async function handleRequest(request: ExtensionRequest, sender: Browser.runtime.MessageSender): Promise { + switch (request.action) { + case 'state.get': return ok(await getState()); + case 'tab.active': { + const boundTabId = senderBoundTabId(sender); + return ok(boundTabId ? await getTab(boundTabId) : await getActiveTab()); + } + case 'tab.get': return ok(await getTab(targetTabId(request.payload.tabId, sender))); + case 'tab.list': return ok((await browser.tabs.query({})).filter((tab) => tab.id && /^https?:/i.test(tab.url || '')).map((tab) => ({ + id: tab.id!, windowId: tab.windowId, title: tab.title || '未命名页面', url: tab.url!, favIconUrl: tab.favIconUrl, lastAccessed: tab.lastAccessed, + }))); + case 'frame.list': return ok(await getFrameInventory(targetTabId(request.payload.tabId, sender)!)); + case 'proxy.save': { + const profile = request.payload; + return ok(await updateState((state) => ({ + ...state, + proxyProfiles: [...state.proxyProfiles.filter((item) => item.id !== profile.id), profile], + }))); + } + case 'proxy.delete': { + const { id } = request.payload; + return ok(await updateState((state) => ({ + ...state, + proxyProfiles: state.proxyProfiles.filter((item) => item.id !== id || item.builtin), + proxyRules: state.proxyRules.filter((rule) => rule.proxyProfileId !== id), + }))); + } + case 'proxy.switch': + await switchProxy(request.payload.id); + return ok(await getState()); + case 'proxy.rule.save': { + const rule = request.payload; + const profiles = (await getState()).proxyProfiles; + if (!profiles.some((profile) => profile.id === rule.proxyProfileId && ['direct', 'fixed_servers'].includes(profile.kind))) { + throw new Error('规则 PAC 只能使用直接连接或固定代理出口'); + } + return ok(await updateState((state) => ({ + ...state, + proxyRules: [...state.proxyRules.filter((item) => item.id !== rule.id), rule], + }))); + } + case 'proxy.rule.delete': { + const { id } = request.payload; + return ok(await updateState((state) => ({ ...state, proxyRules: state.proxyRules.filter((item) => item.id !== id) }))); + } + case 'proxy.rules.apply': + await applyProxyRules(); + return ok(await getState()); + case 'proxy.rules.preview': { + const state = await getState(); + return ok(previewProxyRules(request.payload.url, state.proxyRules, state.proxyProfiles, state.proxyRouting)); + } + case 'proxy.rules.compile': { + const state = await getState(); + return ok(compileProxyRules(state.proxyRules, state.proxyProfiles, state.proxyRouting)); + } + case 'proxy.rules.reorder': { + const ids = request.payload.ids; + const state = await getState(); + if (ids.length !== state.proxyRules.length || new Set(ids).size !== ids.length || ids.some((id) => !state.proxyRules.some((rule) => rule.id === id))) { + throw new Error('规则排序必须包含当前全部规则且不能重复'); + } + const byId = new Map(state.proxyRules.map((rule) => [rule.id, rule])); + return ok(await updateState((current) => ({ + ...current, + proxyRules: ids.map((id, index) => ({ ...byId.get(id)!, priority: (ids.length - index) * 10 })), + }))); + } + case 'proxy.rules.settings': { + const input = request.payload; + const state = await getState(); + if (!state.proxyProfiles.some((profile) => profile.id === input.defaultProfileId && ['direct', 'fixed_servers'].includes(profile.kind))) throw new Error('默认出口必须是直接连接或固定代理'); + return ok(await updateState((current) => ({ ...current, proxyRouting: input }))); + } + case 'proxy.rules.stats': return ok(getProxyRuleStats()); + case 'proxy.rules.stats.clear': + await clearProxyRuleStats(); + return ok(); + case 'proxy.auth.set': + await setProxyAuthPassword(request.payload.profileId, request.payload.password); + return ok({ configured: hasProxyAuthPassword(request.payload.profileId) }); + case 'proxy.auth.status': return ok({ configured: hasProxyAuthPassword(request.payload.profileId) }); + case 'proxy.config.export': { + const state = await getState(); + return ok({ version: 1 as const, profiles: state.proxyProfiles, rules: state.proxyRules, routing: state.proxyRouting }); + } + case 'proxy.config.import': { + const configuration = request.payload.configuration; + const profileIds = new Set(configuration.profiles.map((profile) => profile.id)); + if (profileIds.size !== configuration.profiles.length || !profileIds.has(configuration.routing.defaultProfileId)) throw new Error('代理配置包含重复或缺失的出口 ID'); + if (configuration.rules.some((rule) => !profileIds.has(rule.proxyProfileId))) throw new Error('代理规则引用了不存在的出口'); + const routableIds = new Set(configuration.profiles.filter((profile) => ['direct', 'fixed_servers'].includes(profile.kind)).map((profile) => profile.id)); + if (!routableIds.has(configuration.routing.defaultProfileId) || configuration.rules.some((rule) => !routableIds.has(rule.proxyProfileId))) throw new Error('规则 PAC 只能使用直接连接或固定代理出口'); + return ok(await updateState((current) => ({ + ...current, + proxyProfiles: configuration.profiles, + proxyRules: configuration.rules, + proxyRouting: configuration.routing, + activeProxyId: 'direct', + }))); + } + case 'cookie.list': return ok(await listCookies(request.payload.url)); + case 'cookie.set': return ok(await setCookie(request.payload)); + case 'cookie.remove': { + const input = request.payload; + await removeCookie(input); + return ok(); + } + case 'cookie.removeMany': { + const results = await Promise.allSettled(request.payload.cookies.map((cookie) => removeCookie(cookie))); + const removed = results.filter((result) => result.status === 'fulfilled').length; + return ok({ removed, failed: results.length - removed }); + } + case 'cookie.import': return ok(await importCookies(request.payload.url, request.payload.format, request.payload.text)); + case 'cookie.export': return ok(exportCookies(await listCookies(request.payload.url), request.payload.format, request.payload.includeValues)); + case 'ua.save': { + const rule = request.payload; + const state = await updateState((current) => ({ + ...current, + userAgentRules: [...current.userAgentRules.filter((item) => item.id !== rule.id), rule], + })); + if (state.activeGrant) await startAgentRuntime(state.activeGrant); + await applyUserAgentRules(state.userAgentRules); + return ok(state); + } + case 'ua.delete': { + const state = await updateState((current) => ({ + ...current, + userAgentRules: current.userAgentRules.filter((item) => item.id !== request.payload.id), + })); + await applyUserAgentRules(state.userAgentRules); + return ok(state); + } + case 'ua.apply': { + const state = await getState(); + await applyUserAgentRules(state.userAgentRules); + return ok(state); + } + case 'context.capture': { + const { tabId, frameId, documentId, ...options } = request.payload; + const target = await requiredRequestTarget({ tabId, frameId, documentId }, sender); + const context = await capturePageContext(options, target); + void appendAuditEvent({ + category: 'capability', action: 'context.capture', outcome: 'success', targetTabId: target.tabId, + summary: `${context.document.interactive.length} 个节点,${context.diff.kind}`, + }); + return ok(context); + } + case 'context.node.inspect': { + const input = request.payload; + const target = await requiredRequestTarget(input, sender); + return ok(await inspectPageNode(input.captureId, input.nodeId, target)); + } + case 'context.node.action': { + const input = request.payload; + const target = await requiredRequestTarget(input, sender); + const result = await actOnPageNode(input.captureId, input.nodeId, input.action, target, input.value); + void appendAuditEvent({ + category: 'capability', action: `context.node.${input.action}`, outcome: 'success', targetTabId: target.tabId, + summary: input.nodeId, + }); + return ok(result); + } + case 'context.invoke': { + const input = request.payload; + return ok(await invokePageFunction(input.path, input.args, await requestTarget(input, sender), input.timeoutMs)); + } + case 'context.eval': { + const input = request.payload; + return ok(await evalInPage(input.code, input.mode, await requestTarget(input, sender), input.timeoutMs)); + } + case 'panel.update': { + const input = request.payload; + const policy = (await getEnterprisePolicy()).policy; + return ok(await updateState((current) => applyPolicyToState({ + ...current, floatingPanel: { + enabled: input.enabled ?? current.floatingPanel.enabled, + side: input.side ?? current.floatingPanel.side, + y: typeof input.y === 'number' ? Math.min(Math.max(input.y, 0.08), 0.92) : current.floatingPanel.y, + displayMode: input.displayMode ?? current.floatingPanel.displayMode, + siteMode: input.siteMode ?? current.floatingPanel.siteMode, + siteOrigins: input.siteOrigins + ? [...new Set(input.siteOrigins.map((origin) => new URL(origin).origin))] + : current.floatingPanel.siteOrigins, + shortcutEnabled: input.shortcutEnabled ?? current.floatingPanel.shortcutEnabled, + autoCollapseFullscreen: input.autoCollapseFullscreen ?? current.floatingPanel.autoCollapseFullscreen, + }, + }, policy))); + } + case 'grant.create': { + const input = request.payload; + const boundTabId = senderBoundTabId(sender); + if (boundTabId && input.targets.some((target) => target.tabId !== boundTabId)) { + throw new Error('页面内请求只能授权当前标签页'); + } + const now = Date.now(); + const targets = await createGrantTargets(input.targets); + const policy = (await getEnterprisePolicy()).policy; + const durationMinutes = assertGrantPolicy(policy, { + durationMinutes: input.durationMinutes, + origins: targets.map((target) => target.origin), + programEval: input.scopes.includes('browser.page.eval.program'), + }); + const before = await getState(); + const state = await updateState((current) => ({ + ...current, + activeGrant: { + id: crypto.randomUUID(), + taskId: input.taskId || `manual-${crypto.randomUUID()}`, + targets, + scopes: [...new Set(input.scopes)], + createdAt: now, + expiresAt: now + durationMinutes * 60_000, + }, + handoff: current.handoff?.state === 'waiting_for_user' + ? { ...current.handoff, state: 'cancelled', resolvedAt: now } + : current.handoff, + })); + if (before.activeGrant) { + await Promise.all([ + stopNetworkCapturesForGrant(before.activeGrant.id), + stopPageObservationsForGrant(before.activeGrant.id), + ]); + } + if (before.handoff?.state === 'waiting_for_user' && state.handoff) { + await browser.action.setBadgeText({ text: '', tabId: before.handoff.target.tabId }); + engineBridge.emitEvent('browser.handoff.changed', state.handoff); + void appendAuditEvent({ + category: 'handoff', action: 'handoff.cancelled', outcome: 'cancelled', + taskId: before.handoff.taskId, targetTabId: before.handoff.target.tabId, + summary: '创建新授权会话时取消', + }); + } + void appendAuditEvent({ + category: 'grant', action: 'grant.create', outcome: 'success', taskId: state.activeGrant?.taskId, + targetTabId: state.activeGrant?.targets[0]?.tabId, + summary: `${state.activeGrant?.targets.length || 0} 个标签页,${state.activeGrant?.scopes.length || 0} 项能力`, + }); + return ok(state); + } + case 'grant.revoke': { + const before = await getState(); + engineBridge.cancelActiveRequests(); + const state = await updateState((current) => ({ + ...current, + activeGrant: undefined, + handoff: current.handoff?.state === 'waiting_for_user' + ? { ...current.handoff, state: 'cancelled', resolvedAt: Date.now() } + : current.handoff, + })); + if (before.activeGrant) { + await Promise.all([ + stopNetworkCapturesForGrant(before.activeGrant.id), + stopPageObservationsForGrant(before.activeGrant.id), + ]); + } + await setAgentRuntimeState('revoked', before.activeGrant); + if (state.handoff && before.handoff?.state === 'waiting_for_user') engineBridge.emitEvent('browser.handoff.changed', state.handoff); + if (before.handoff?.state === 'waiting_for_user') { + await browser.action.setBadgeText({ text: '', tabId: before.handoff.target.tabId }); + void appendAuditEvent({ + category: 'handoff', action: 'handoff.cancelled', outcome: 'cancelled', + taskId: before.handoff.taskId, targetTabId: before.handoff.target.tabId, + summary: '撤销授权会话时取消', + }); + } + void appendAuditEvent({ + category: 'grant', action: 'grant.revoke', outcome: 'success', taskId: before.activeGrant?.taskId, + targetTabId: before.activeGrant?.targets[0]?.tabId, + }); + return ok(state); + } + case 'handoff.resolve': { + const input = request.payload; + const state = await updateState((current) => { + if (!current.handoff || current.handoff.id !== input.id || current.handoff.state !== 'waiting_for_user') { + throw new ExtensionError('handoff_not_waiting', '人工接管请求不存在或已经结束'); + } + return { + ...current, + handoff: { ...current.handoff, state: input.outcome, resolvedAt: Date.now() }, + }; + }); + const handoff = state.handoff!; + await setAgentRuntimeState(input.outcome === 'completed' ? 'running' : 'paused', state.activeGrant); + await browser.action.setBadgeText({ text: '', tabId: handoff.target.tabId }); + engineBridge.emitEvent('browser.handoff.changed', handoff); + void appendAuditEvent({ + category: 'handoff', action: `handoff.${input.outcome}`, outcome: input.outcome === 'completed' ? 'success' : 'cancelled', + taskId: handoff.taskId, targetTabId: handoff.target.tabId, + }); + return ok(state); + } + case 'network.capture.start': { + const input = request.payload; + const target = await requiredRequestTarget(input, sender); + const status = await startNetworkCapture(target, input); + void appendAuditEvent({ + category: 'capability', action: 'network.capture.start', outcome: 'success', targetTabId: target.tabId, + summary: input.captureHeaders || input.captureBody ? '包含用户明确启用的敏感字段' : '仅元数据', + }); + return ok(status); + } + case 'network.capture.status': return ok(await networkCaptureStatus(await requiredRequestTarget(request.payload, sender))); + case 'network.capture.list': { + const target = await requiredRequestTarget(request.payload, sender); + return ok(await listNetworkRequests(target, request.payload.limit)); + } + case 'network.capture.clear': { + const target = await requiredRequestTarget(request.payload, sender); + const status = await clearNetworkRequests(target); + void appendAuditEvent({ category: 'capability', action: 'network.capture.clear', outcome: 'success', targetTabId: target.tabId }); + return ok(status); + } + case 'network.capture.stop': { + const target = await requiredRequestTarget(request.payload, sender); + const status = await stopNetworkCapture(target); + void appendAuditEvent({ category: 'capability', action: 'network.capture.stop', outcome: 'success', targetTabId: target.tabId }); + return ok(status); + } + case 'network.capture.export': { + const target = await requiredRequestTarget(request.payload, sender); + const exported = await exportNetworkRequest(target, request.payload.id); + void appendAuditEvent({ category: 'capability', action: 'network.capture.export', outcome: 'success', targetTabId: target.tabId }); + return ok(exported); + } + case 'network.capture.send': { + const target = await requiredRequestTarget(request.payload, sender); + try { + const exported = await exportNetworkRequest(target, request.payload.id); + const result = await engineBridge.requestEngine('yakit.web_fuzzer.open', { + rawRequestBase64: exported.rawRequestBase64, + isHttps: exported.isHttps, + tabName: `Browser · ${new URL(exported.url).hostname}`, + }); + void appendAuditEvent({ + category: 'capability', action: 'network.capture.send_to_fuzzer', outcome: 'success', targetTabId: target.tabId, + summary: `Web Fuzzer ${result.pageId}`, + }); + return ok(result); + } catch (error) { + void appendAuditEvent({ + category: 'capability', action: 'network.capture.send_to_fuzzer', outcome: 'error', + targetTabId: target.tabId, errorCode: errorCode(error), + }); + throw error; + } + } + case 'network.capture.poc': { + const target = await requiredRequestTarget(request.payload, sender); + const result = await engineBridge.requestEngine( + 'yakit.poc.generate', + await capturedRequestEnginePayload(target, request.payload.id, false), + ); + void appendAuditEvent({ category: 'capability', action: 'network.capture.generate_poc', outcome: 'success', targetTabId: target.tabId }); + return ok(result); + } + case 'network.capture.analysis': { + const target = await requiredRequestTarget(request.payload, sender); + const result = await engineBridge.requestEngine( + 'yakit.browser_request.prepare_analysis', + await capturedRequestEnginePayload(target, request.payload.id, true), + ); + void appendAuditEvent({ category: 'capability', action: 'network.capture.prepare_analysis', outcome: 'success', targetTabId: target.tabId }); + return ok(result); + } + case 'observation.start': { + const input = request.payload; + const target = await requiredRequestTarget(input, sender); + const status = await startPageObservation(target, input); + void appendAuditEvent({ + category: 'capability', action: 'observation.start', outcome: 'success', targetTabId: target.tabId, + summary: input.captureValues ? '包含用户明确启用的短时值预览' : '仅元数据', + }); + return ok(status); + } + case 'observation.status': return ok(await pageObservationStatus(await requiredRequestTarget(request.payload, sender))); + case 'observation.list': { + const target = await requiredRequestTarget(request.payload, sender); + return ok(await listPageObservations(target, request.payload.limit, true)); + } + case 'observation.clear': { + const target = await requiredRequestTarget(request.payload, sender); + const status = await clearPageObservations(target); + void appendAuditEvent({ category: 'capability', action: 'observation.clear', outcome: 'success', targetTabId: target.tabId }); + return ok(status); + } + case 'observation.stop': { + const target = await requiredRequestTarget(request.payload, sender); + const status = await stopPageObservation(target); + void appendAuditEvent({ category: 'capability', action: 'observation.stop', outcome: 'success', targetTabId: target.tabId }); + return ok(status); + } + case 'audit.list': return ok(await listAuditEvents(request.payload.limit)); + case 'audit.clear': { + await clearAuditEvents(); + return ok(); + } + case 'agent.runtime.get': return ok(await getAgentRuntime()); + case 'agent.pause': { + const state = await getState(); + if (!state.activeGrant) throw new ExtensionError('grant_expired', '没有可暂停的浏览器共享会话'); + engineBridge.cancelActiveRequests(); + const runtime = await setAgentRuntimeState('paused', state.activeGrant); + void appendAuditEvent({ category: 'grant', action: 'agent.pause', outcome: 'success', taskId: state.activeGrant.taskId }); + return ok(runtime); + } + case 'agent.resume': { + const state = await getState(); + if (!state.activeGrant || state.activeGrant.expiresAt <= Date.now()) throw new ExtensionError('grant_expired', '浏览器共享会话不存在或已经过期'); + const runtime = await setAgentRuntimeState('running', state.activeGrant); + void appendAuditEvent({ category: 'grant', action: 'agent.resume', outcome: 'success', taskId: state.activeGrant.taskId }); + return ok(runtime); + } + case 'agent.actions.clear': return ok(await clearAgentActions()); + case 'policy.status': return ok(await getEnterprisePolicy()); + case 'diagnostics.export': return ok(await createDiagnosticsBundle(engineBridge.getStatus())); + case 'metrics.get': return ok(await getRuntimeMetrics()); + case 'metrics.reset': return ok(await resetRuntimeMetrics()); + case 'bridge.config.save': { + const config = applyPolicyToBridge(request.payload, (await getEnterprisePolicy()).policy); + const state = await updateState((current) => ({ ...current, bridge: config })); + if (config.autoConnect && config.pairedEngine) await engineBridge.connect(config); + else engineBridge.disconnect(); + return ok(state); + } + case 'bridge.pair': { + const status = await engineBridge.startPairing(); + void appendAuditEvent({ category: 'bridge', action: 'bridge.pair', outcome: 'success' }); + return ok(status); + } + case 'bridge.pair.cancel': return ok(engineBridge.cancelPairing()); + case 'bridge.pair.status': return ok(engineBridge.getPairingStatus()); + case 'bridge.unpair': { + await engineBridge.unpair(); + void appendAuditEvent({ category: 'bridge', action: 'bridge.unpair', outcome: 'success' }); + return ok(await getState()); + } + case 'bridge.connect': { + await engineBridge.connect(); + void appendAuditEvent({ category: 'bridge', action: 'bridge.connect', outcome: 'success' }); + return ok(engineBridge.getStatus()); + } + case 'bridge.disconnect': { + engineBridge.disconnect(); + void appendAuditEvent({ category: 'bridge', action: 'bridge.disconnect', outcome: 'success' }); + return ok(engineBridge.getStatus()); + } + case 'bridge.status': return ok(engineBridge.getStatus()); + default: return fail('未知扩展操作'); + } +} + +export async function runBackground(): Promise { + recordServiceWorkerStart(); + browser.runtime.onMessage.addListener((input: unknown, sender: Browser.runtime.MessageSender, sendResponse) => { + if (['bridge.status.changed', 'bridge.pairing.status.changed'].includes((input as { action?: string })?.action || '')) return undefined; + void Promise.resolve().then(() => parseExtensionRequest(input)).then((request) => handleRequest(request, sender)).then(sendResponse).catch((error) => sendResponse(fail(error))); + return true; + }); + const storedState = await getState(); + const state = applyPolicyToState(storedState, (await getEnterprisePolicy()).policy); + if (JSON.stringify(state.bridge) !== JSON.stringify(storedState.bridge) || JSON.stringify(state.floatingPanel) !== JSON.stringify(storedState.floatingPanel)) { + await updateState(() => state); + } + await applyUserAgentRules(state.userAgentRules).catch(console.error); + if (state.bridge.autoConnect && state.bridge.pairedEngine) await engineBridge.connect(state.bridge).catch(console.error); +} diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 8e0e0f1..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/components/ProxySwitch/index.css b/src/components/ProxySwitch/index.css deleted file mode 100644 index 0a79d45..0000000 --- a/src/components/ProxySwitch/index.css +++ /dev/null @@ -1,385 +0,0 @@ -.proxy-container { - min-width: 200px; - background: white; -} - -.proxy-menu { - border: none !important; - box-shadow: none !important; - overflow: hidden; - padding: 0 !important; - margin: 0 !important; -} - -.menu-item { - height: 40px !important; - line-height: 40px !important; - margin: 0 !important; - padding: 0 16px !important; -} - -.menu-item .anticon { - font-size: 16px; - color: var(--yakit-primary); - margin-right: 8px; -} - -.menu-item-label { - font-size: 14px; - color: #333; -} - -.menu-item:hover { - background-color: var(--yakit-primary-5) !important; -} - -.menu-item:hover .anticon, -.menu-item:hover .menu-item-label { - color: var(--yakit-primary) !important; -} - -/* 选中状态 */ -.menu-item.ant-menu-item-selected { - background-color: var(--yakit-primary) !important; -} - -.menu-item.ant-menu-item-selected .anticon, -.menu-item.ant-menu-item-selected .menu-item-label { - color: white !important; -} - -.menu-item.ant-menu-item-selected:hover { - background-color: var(--yakit-primary-hover) !important; -} - -/* 分隔线 */ -.ant-menu-item-divider { - margin: 4px 0 !important; - border-color: #EAECF3 !important; -} - -/* 设置选项 */ -.menu-item-setting { - border-top: 1px solid #EAECF3; - margin-top: 4px !important; -} - -.menu-item-setting .anticon { - color: #666; -} - -.menu-item-setting:hover { - background-color: var(--yakit-primary-5) !important; -} - -.menu-item-setting:hover .anticon, -.menu-item-setting:hover .menu-item-label { - color: var(--yakit-primary) !important; -} - -/* 调整图标大小和对齐 */ -.anticon { - font-size: 16px; -} - -/* 添加以下样式来确保下拉菜单显示在正确的位置 */ -.ant-dropdown { - position: absolute !important; - top: 100% !important; - left: 0 !important; - width: 100% !important; - min-width: 200px !important; -} - -.dropdown-content { - background: white; - border-radius: 4px; - box-shadow: 0 3px 6px -4px rgba(0,0,0,0.12), - 0 6px 16px 0 rgba(0,0,0,0.08), - 0 9px 28px 8px rgba(0,0,0,0.05); - padding: 4px; -} - -/* 确保容器不会限制弹出层 */ -.proxy-container { - min-width: 200px; - background: white; - border-radius: 4px; - position: static; -} - -/* 添加这个样式来确保下拉菜单显示在正确的位置 */ -body { - position: relative; -} - -.ant-menu { - border: none !important; - box-shadow: none !important; - padding: 0 !important; - width: 100% !important; - background: white !important; -} - -.ant-menu-item { - height: 36px !important; - line-height: 36px !important; - margin: 4px 8px !important; - padding: 0 16px !important; - display: flex !important; - align-items: center !important; - border-radius: 6px !important; - transition: all 0.2s ease-in-out !important; -} - -.ant-menu-item:hover { - background-color: #f5f5f5 !important; - color: var(--yakit-primary) !important; -} - -.ant-menu-item.ant-menu-item-selected { - background-color: var(--yakit-primary) !important; - color: white !important; - border-radius: 6px !important; -} - -.ant-menu-item.ant-menu-item-selected:hover { - background-color: var(--yakit-primary-hover) !important; -} - -.ant-menu-item .anticon, -.ant-menu-item img { - font-size: 16px; - margin-right: 8px; - transition: all 0.2s ease-in-out !important; -} - -.ant-menu-item.ant-menu-item-selected .anticon, -.ant-menu-item.ant-menu-item-selected img { - color: white; -} - -.ant-menu-item-divider { - margin: 4px 0 !important; - height: 1px !important; - background-color: #f0f0f0 !important; -} - -/* Checked item style (for the 2080 with checkmark) */ -.ant-menu-item.checked::after { - content: "✓"; - position: absolute; - right: 16px; - color: var(--yakit-primary); - font-weight: bold; -} - -.ant-menu-item.ant-menu-item-selected.checked::after { - color: white; -} - -/* Loading state */ -.loading-overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(255, 255, 255, 0.7); - display: flex; - justify-content: center; - align-items: center; - z-index: 10; - font-size: 14px; - color: var(--yakit-primary); - opacity: 0; - animation: fadeIn 0.2s ease-in-out forwards; -} - -@keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } -} - -/* Remove conflicting styles */ -.panel-watermark { - display: none; -} - -/* Improve bottom actions */ -.ant-menu-item:nth-last-child(1), -.ant-menu-item:nth-last-child(2) { - border-top: 1px solid #f0f0f0; - margin-top: 4px !important; -} - -.ant-menu-item:nth-last-child(1) .anticon, -.ant-menu-item:nth-last-child(2) .anticon { - color: #666; -} - -.ant-menu-item:nth-last-child(1):hover .anticon, -.ant-menu-item:nth-last-child(2):hover .anticon { - color: var(--yakit-primary); -} - -/* 全局样式重置,确保菜单项样式不受默认样式影响 */ -.proxy-switch-container { - position: relative; - width: 100%; - overflow: hidden; - border-radius: 0; - box-shadow: none; -} - -/* 确保菜单没有边框和阴影 */ -.proxy-switch-container .ant-menu { - border: none !important; - border-right: none !important; - box-shadow: none !important; -} - -/* 确保所有菜单项正确对齐和布局 */ -.proxy-switch-container .ant-menu-item { - margin: 4px 8px !important; - border-radius: 6px !important; - height: 36px !important; - line-height: 36px !important; - position: relative !important; -} - -/* 图标对齐 */ -.proxy-switch-container .ant-menu-item .anticon, -.proxy-switch-container .ant-menu-item img { - position: absolute !important; - left: 16px !important; - top: 50% !important; - transform: translateY(-50%) !important; -} - -/* 选中态和悬停态 */ -.proxy-switch-container .ant-menu-item.ant-menu-item-selected { - background-color: var(--yakit-primary) !important; - color: white !important; -} - -/* 选中态下的YAK图标变为白色 */ -.proxy-switch-container .ant-menu-item.ant-menu-item-selected img { - filter: brightness(0) invert(1) !important; -} - -.proxy-switch-container .ant-menu-item:hover { - background-color: #f5f5f5 !important; -} - -.proxy-switch-container .ant-menu-item.ant-menu-item-selected:hover { - background-color: var(--yakit-primary-hover) !important; -} - -/* 确保分割线样式 */ -.proxy-switch-container .ant-menu-item-divider { - margin: 4px 0 !important; - height: 1px !important; - background-color: #f0f0f0 !important; -} - -.panel-watermark { - position: absolute; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - opacity: 0.03; - pointer-events: none; - object-fit: contain; - object-position: right bottom; - z-index: 0; -} - -/* 确保菜单项在水印上层 */ -.ant-menu-item { - position: relative; - z-index: 1; - background: transparent !important; -} - -/* 确保分割线在水印上层 */ -.ant-menu-item-divider { - position: relative; - z-index: 1; -} - -/* 为[直接连接]菜单项添加橙色背景,但仅在被选中时 */ -.ant-menu .ant-menu-item.menu-id-direct.ant-menu-item-selected { - background-color: var(--yakit-primary) !important; - color: white !important; -} - -.ant-menu .ant-menu-item.menu-id-direct.ant-menu-item-selected .anticon, -.ant-menu .ant-menu-item.menu-id-direct.ant-menu-item-selected span { - color: white !important; -} - -/* 覆盖 Ant Design 的宽度计算,确保菜单项占据全宽 */ -.proxy-switch-container .ant-menu .ant-menu-item, -.ant-menu-light .ant-menu-item, -.ant-menu-vertical .ant-menu-item, -.ant-menu-inline .ant-menu-item, -.ant-menu .ant-menu-item { - width: 100% !important; - margin-inline: 0 !important; - margin-block: 4px !important; - text-align: center !important; - height: 40px !important; - line-height: 40px !important; - padding-inline: 16px !important; - overflow: hidden !important; - text-overflow: ellipsis !important; -} - -/* 让文本的容器也占满全宽 */ -.proxy-switch-container .ant-menu .ant-menu-item .ant-menu-title-content, -.ant-menu .ant-menu-item .ant-menu-title-content { - display: block !important; - width: 100% !important; - text-align: center !important; -} - -/* 活动项样式 */ -.ant-menu .ant-menu-item.active-item::after { - content: "✓"; - position: absolute; - right: 16px; - top: 50%; - transform: translateY(-50%); - color: var(--yakit-primary); - font-weight: bold; -} - -.ant-menu .ant-menu-item.ant-menu-item-selected.active-item::after { - color: white; -} - -.active-item { - background-color: var(--yakit-primary) !important; - color: white !important; - font-weight: bold; - transition: background-color 0.2s ease-in-out !important; -} - -.active-item img { - filter: brightness(0) invert(1); -} - -.active-item .anticon { - color: white !important; -} - -.proxy-menu .ant-menu-item { - transition: all 0.2s ease-in-out !important; -} - -/* 添加悬停效果 */ -.proxy-menu .ant-menu-item:hover { - background-color: rgba(242, 139, 68, 0.1) !important; -} \ No newline at end of file diff --git a/src/components/ProxySwitch/index.tsx b/src/components/ProxySwitch/index.tsx deleted file mode 100644 index 49b31e1..0000000 --- a/src/components/ProxySwitch/index.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import React, {useEffect, useState, useRef} from "react"; -import {Menu} from "antd"; -import { - DisconnectOutlined, - SettingOutlined, - PlusOutlined, - CheckOutlined, -} from "@ant-design/icons"; -import {browser} from "wxt/browser"; -import type {MenuProps} from "antd"; -import type {ProxyConfig} from "@/types/proxy"; -import {ContentActionType, ProxyActionType} from "@/types/action"; -import {getAllProxyConfigs, getCurrentProxy} from "@/utils/storage"; - -import "./index.css"; - -// YAK 图标 URL -const YAK_ICON_URL = browser.runtime.getURL("/yak.svg"); - -// 固定的代理模式 -const FIXED_MODES = [ - { - key: "direct", - name: "[直接连接]", - icon: , - color: "#666", - config: { - id: "direct", - name: "[直接连接]", - proxyType: "direct", - enabled: false, - }, - }, - { - key: "system", - name: "[系统代理]", - icon: , - color: "#666", - config: { - id: "system", - name: "[系统代理]", - proxyType: "system", - enabled: false, - }, - }, -]; - -interface CustomProxy { - key: string; - name: string; - color: string; - config: ProxyConfig; - enabled?: boolean; -} - -export const ProxySwitch: React.FC = () => { - const [initialized, setInitialized] = useState(false); - const [currentMode, setCurrentMode] = useState("direct"); // 默认选中直接连接 - const [customProxies, setCustomProxies] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const loadingTimeoutRef = useRef(null); - - // 监听存储变化 - useEffect(() => { - const handleMessage = (message: any) => { - if ( - message.action === ContentActionType.PROXY_CONFIGS_UPDATED && - message.source !== "proxy_switch" - ) { - console.log("proxy_switch 收到代理配置更新消息", message); - - loadCustomProxies(); - loadProxyStatus(); - } - }; - - browser.runtime.onMessage.addListener(handleMessage); - return () => { - browser.runtime.onMessage.removeListener(handleMessage); - // 清除可能存在的超时计时器 - if (loadingTimeoutRef.current) { - clearTimeout(loadingTimeoutRef.current); - } - }; - }, []); - - // 初始化 - useEffect(() => { - let mounted = true; - - const init = async () => { - try { - await loadProxyStatus(); - if (mounted) { - await loadCustomProxies(); - setInitialized(true); - } - } catch (error) { - console.error("初始化失败:", error); - if (mounted) { - setInitialized(true); - } - } - }; - - init(); - - return () => { - mounted = false; - }; - }, []); - - // 获取当前代理状态 - const loadProxyStatus = async () => { - try { - // 先尝试从后台脚本获取当前代理状态 - const response = await browser.runtime.sendMessage({ - action: ProxyActionType.GET_PROXY_STATUS, - }); - console.log("proxy_switch 获取当前代理状态", response); - - if (response && response.success) { - const activeMode = response.data.mode; - console.log("获取到当前代理模式:", activeMode); - setCurrentMode(activeMode); - return; - } - - // 如果后台脚本没有返回,则从存储中获取当前代理 - const currentProxy = await getCurrentProxy(); - if (currentProxy) { - console.log("从存储获取到当前代理:", currentProxy.id); - setCurrentMode(currentProxy.id); - } else { - console.log("未找到当前代理,使用默认值 direct"); - setCurrentMode("direct"); - } - } catch (error) { - console.error("Error loading proxy status:", error); - setCurrentMode("direct"); - } - }; - - // 加载自定义代理配置 - const loadCustomProxies = async () => { - try { - // 使用存储API获取所有代理配置 - const configs = await getAllProxyConfigs(); - - // 处理代理配置 - const proxies = configs - .filter( - (proxy: ProxyConfig) => !["direct", "system"].includes(proxy.id) - ) - .map( - (proxy: ProxyConfig): CustomProxy => ({ - key: proxy.id, - name: proxy.name, - color: "#1890ff", - config: proxy, - enabled: proxy.enabled, - }) - ); - setCustomProxies(proxies); - console.log("proxy_switch proxies", proxies); - - // 查找并设置已启用的代理 - const enabledProxy = configs.find((proxy: ProxyConfig) => proxy.enabled); - if (enabledProxy) { - console.log("proxy_switch enabledProxy", enabledProxy); - setCurrentMode(enabledProxy.id); - } - } catch (error) { - console.error("Error loading custom proxies:", error); - setCustomProxies([]); - } - }; - - // 处理代理模式变更 - const handleModeChange = async (mode: string) => { - if (mode === "setting") { - // 打开设置页面 - await browser.runtime.openOptionsPage?.(); - return; - } - - if (mode === "add") { - // 打开添加代理表单 - try { - const [activeTab] = await browser.tabs.query({ - active: true, - currentWindow: true, - }); - - const optionsUrl = browser.runtime.getURL("/options.html"); - - if (activeTab?.url === optionsUrl) { - browser.tabs.sendMessage(activeTab.id!, { - action: ContentActionType.TRIGGER_ADD_PROXY, - }); - } else { - await browser.tabs.create({ - url: optionsUrl, - }); - } - } catch (error) { - console.error("Failed to get current tab:", error); - } - return; - } - console.log("proxy_switch 处理代理模式变更", mode); - - // 如果当前已经是选中的模式,不做任何操作 - if (mode === currentMode) return; - - // 清除之前可能存在的加载超时 - if (loadingTimeoutRef.current) { - clearTimeout(loadingTimeoutRef.current); - } - - try { - // 先更新UI,让用户感知到变化 - setCurrentMode(mode); - setIsLoading(true); - - // 设置超时保护,确保加载状态最终会被清除 - loadingTimeoutRef.current = window.setTimeout(() => { - setIsLoading(false); - }, 5000); // 5秒超时保护 - - // 发送切换代理请求 - const response = await browser.runtime.sendMessage({ - action: ProxyActionType.SWITCH_PROXY, - mode, - source: "proxy_switch", - }); - - // 请求完成后,清除超时保护 - if (loadingTimeoutRef.current) { - clearTimeout(loadingTimeoutRef.current); - loadingTimeoutRef.current = null; - } - - if (!response || !response.success) { - // 如果失败,恢复原状态 - console.error("Failed to switch proxy mode"); - await loadProxyStatus(); // 重新加载正确的状态 - } else { - console.log("代理模式切换成功:", mode); - // 不加 setTimeout firefox UI 会无法渲染选中状态 - setTimeout(() => { - setCurrentMode((preMode) => { - return mode === preMode ? preMode : mode; - }); - }, 20); - } - } catch (error) { - console.error(`Error switching to proxy mode ${mode}:`, error); - await loadProxyStatus(); // 出错时重新加载正确的状态 - } finally { - // 无论如何,最终要关闭加载状态 - setIsLoading(false); - } - }; - - // 构建菜单项 - const buildMenuItems = () => { - const items: MenuProps["items"] = [ - ...FIXED_MODES.map((mode) => ({ - key: mode.key, - label: mode.name, - icon: mode.icon, - className: `${currentMode === mode.key ? "active-item" : ""} menu-id-${ - mode.key - }`, - })), - {type: "divider"}, - ]; - - // 添加自定义代理 - if (customProxies.length > 0) { - items.push( - ...customProxies.map((proxy) => { - // 构建提示信息:显示代理协议、主机和端口 - const tooltipText = - proxy.config.proxyType === "fixed_servers" && - proxy.config.host && - proxy.config.port - ? `${proxy.config.scheme || "http"}://${proxy.config.host}:${ - proxy.config.port - }` - : proxy.config.proxyType === "pac_script" - ? "PAC脚本代理" - : proxy.config.proxyType === "auto_detect" - ? "自动检测代理" - : ""; - - // Firefox 兼容性:确保 active-item 类始终应用正确 - const isActive = currentMode === proxy.key; - - return { - key: proxy.key, - label: proxy.name, - icon: ( - YAK - ), - className: `${isActive ? "active-item" : ""} menu-id-${proxy.key}`, - title: tooltipText, // 添加悬停提示 - }; - }) - ); - items.push({type: "divider"}); - } - - // 添加设置选项 - items.push({ - key: "setting", - label: "代理设置", - icon: , - className: "menu-id-setting", - }); - - // 添加新建代理选项 - items.push({ - key: "add", - label: "添加代理", - icon: , - className: "menu-id-add", - }); - - return items; - }; - - // 只在加载完成后渲染内容 - if (!initialized) { - return
加载中...
; - } - - return ( -
- handleModeChange(key)} - /> - {isLoading && ( -
- 切换中... -
- )} -
- ); -}; diff --git a/src/components/brand/Brand.tsx b/src/components/brand/Brand.tsx new file mode 100644 index 0000000..370a8ec --- /dev/null +++ b/src/components/brand/Brand.tsx @@ -0,0 +1,21 @@ +import { cn } from '@/lib/cn'; + +export function YakMark({ className, alt = 'Yak' }: { className?: string; alt?: string }) { + return {alt}; +} + +export function YakitMark({ className }: { className?: string }) { + return Yakit; +} + +export function ProductBrand({ compact = false, className }: { compact?: boolean; className?: string }) { + return ( +
+ + + Yakit Browser Agent + {!compact && Authenticated browser security workspace} + +
+ ); +} diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..0d2c1a5 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,6 @@ +import type { HTMLAttributes } from 'react'; +import { cn } from '@/lib/cn'; + +export function Badge({ className, ...props }: HTMLAttributes) { + return ; +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..d065659 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,31 @@ +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; +import type { ButtonHTMLAttributes } from 'react'; +import { cn } from '@/lib/cn'; + +const buttonVariants = cva('ui-button', { + variants: { + variant: { + primary: 'ui-button--primary', + secondary: 'ui-button--secondary', + ghost: 'ui-button--ghost', + danger: 'ui-button--danger', + }, + size: { + sm: 'ui-button--sm', + md: 'ui-button--md', + icon: 'ui-button--icon', + }, + }, + defaultVariants: { variant: 'secondary', size: 'md' }, +}); + +export interface ButtonProps + extends ButtonHTMLAttributes, VariantProps { + asChild?: boolean; +} + +export function Button({ className, variant, size, asChild, ...props }: ButtonProps) { + const Component = asChild ? Slot : 'button'; + return ; +} diff --git a/src/components/ui/field.tsx b/src/components/ui/field.tsx new file mode 100644 index 0000000..5fbc983 --- /dev/null +++ b/src/components/ui/field.tsx @@ -0,0 +1,16 @@ +import type { LabelHTMLAttributes, ReactNode } from 'react'; +import { cn } from '@/lib/cn'; + +export function Field({ label, hint, children, className, ...props }: { + label: string; + hint?: string; + children: ReactNode; +} & Omit, 'children'>) { + return ( + + ); +} diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx new file mode 100644 index 0000000..c2461f0 --- /dev/null +++ b/src/components/ui/switch.tsx @@ -0,0 +1,11 @@ +import * as SwitchPrimitive from '@radix-ui/react-switch'; +import type { ComponentProps } from 'react'; +import { cn } from '@/lib/cn'; + +export function Switch({ className, ...props }: ComponentProps) { + return ( + + + + ); +} diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..7e36665 --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,17 @@ +import * as TabsPrimitive from '@radix-ui/react-tabs'; +import type { ComponentProps } from 'react'; +import { cn } from '@/lib/cn'; + +export const Tabs = TabsPrimitive.Root; + +export function TabsList({ className, ...props }: ComponentProps) { + return ; +} + +export function TabsTrigger({ className, ...props }: ComponentProps) { + return ; +} + +export function TabsContent({ className, ...props }: ComponentProps) { + return ; +} diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx new file mode 100644 index 0000000..261febd --- /dev/null +++ b/src/components/ui/tooltip.tsx @@ -0,0 +1,23 @@ +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; +import type { ComponentProps, ReactNode } from 'react'; +import { cn } from '@/lib/cn'; + +export const TooltipProvider = TooltipPrimitive.Provider; + +export function Tooltip({ label, children, side = 'top' }: { + label: string; + children: ReactNode; + side?: ComponentProps['side']; +}) { + return ( + + {children} + + + {label} + + + + + ); +} diff --git a/src/entrypoints/agent.content/index.ts b/src/entrypoints/agent.content/index.ts new file mode 100644 index 0000000..f8760ba --- /dev/null +++ b/src/entrypoints/agent.content/index.ts @@ -0,0 +1,245 @@ +import { browser } from 'wxt/browser'; +import { installPageWorldBridge } from '@/features/page-context/content-bridge'; +import { isStateStorageChange } from '@/protocol/storage'; +import type { ActiveTabInfo, BridgeStatus, ExtensionState } from '@/types/models'; + +const PANEL_IDLE_UNLOAD_MS = 60_000; + +const shellCss = ` + :host { all: initial; position: fixed !important; inset: 0 !important; z-index: 2147483646 !important; pointer-events: none !important; } + .floating-panel { position: fixed; width: 46px; height: 46px; transform: translateY(-50%); pointer-events: auto; transition: width .16s ease; } + .floating-panel--left { left: 0; } + .floating-panel--right { right: 0; } + .floating-panel.is-expanded { width: min(326px, calc(100vw - 8px)); } + .floating-panel__header { position: absolute; top: 0; z-index: 2; width: 46px; height: 46px; touch-action: none; } + .floating-panel--left .floating-panel__header { left: 0; } + .floating-panel--right .floating-panel__header { right: 0; } + .floating-panel__brand { position: relative; width: 46px; height: 46px; padding: 0; display: grid; place-items: center; border: 1px solid #d7dce1; background: #fff; cursor: pointer; box-shadow: 0 8px 20px rgba(20,24,28,.18); transition: background-color .16s ease, box-shadow .16s ease; } + .floating-panel__brand:hover { background: #f1f3f5; } + :host([data-theme='dark']) .floating-panel__brand { border-color: #343a40; background: #1d232b; } + :host([data-theme='dark']) .floating-panel__brand:hover { background: #262d36; } + .floating-panel--left .floating-panel__brand { border-left: 0; border-radius: 0 23px 23px 0; } + .floating-panel--right .floating-panel__brand { border-right: 0; border-radius: 23px 0 0 23px; } + .floating-panel.is-expanded .floating-panel__brand { box-shadow: none; } + .floating-panel__brand:focus-visible { outline: 2px solid #ee7815; outline-offset: -3px; } + .floating-panel__brand img { width: 42px; height: 42px; display: block; object-fit: contain; pointer-events: none; } + .floating-panel__signal { position: absolute; right: 5px; bottom: 5px; width: 7px; height: 7px; border: 1px solid #fff; border-radius: 50%; background: #90979e; } + :host([data-theme='dark']) .floating-panel__signal { border-color: #1d232b; } + .floating-panel__signal.connected { background: #45b77d; } + .floating-panel__signal.connecting, .floating-panel__signal.negotiating { background: #e3a632; } + .floating-panel__signal.error { background: #dc5e5e; } + iframe { width: 100%; height: 320px; display: block; border: 0; border-radius: 8px; box-shadow: 0 10px 28px rgba(22,28,33,.18); } +`; + +async function send(action: string, payload?: unknown): Promise { + const response = await browser.runtime.sendMessage({ action, payload }) as { ok?: boolean; data?: T; error?: string }; + if (!response?.ok) throw new Error(response?.error || action); + return response.data as T; +} + +export default defineContentScript({ + matches: ['http://*/*', 'https://*/*'], + runAt: 'document_start', + + async main(ctx) { + if ((import.meta.env.FIREFOX && import.meta.env.MODE !== 'store') + || (!import.meta.env.FIREFOX && import.meta.env.MODE !== 'production' && import.meta.env.MODE !== 'store')) { + await installPageWorldBridge(ctx).catch((error) => { + console.warn('[Yakit Browser Agent] MAIN-world bridge is unavailable; continuing without page Eval/Invoke.', error); + }); + } + + const host = document.createElement('yakit-browser-agent'); + const shadow = host.attachShadow({ mode: 'open' }); + const style = document.createElement('style'); + style.textContent = shellCss; + const panel = document.createElement('div'); + panel.className = 'floating-panel floating-panel--right'; + const header = document.createElement('div'); + header.className = 'floating-panel__header'; + const launcher = document.createElement('button'); + launcher.type = 'button'; + launcher.className = 'floating-panel__brand'; + launcher.setAttribute('aria-label', '展开 Yakit Browser Agent'); + const logo = document.createElement('img'); + logo.src = browser.runtime.getURL('/yak.svg'); + logo.alt = 'Yak'; + logo.draggable = false; + const signal = document.createElement('span'); + signal.className = 'floating-panel__signal disconnected'; + launcher.append(logo, signal); + header.append(launcher); + panel.append(header); + shadow.append(style, panel); + document.documentElement.append(host); + + // Launcher theme follows the extension appearance setting (settings.appearance.v1), falling back to the OS scheme. + const themeKey = 'settings.appearance.v1'; + const applyTheme = (theme?: string) => { + host.dataset.theme = theme === 'light' || theme === 'dark' + ? theme + : (globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); + }; + void browser.storage.local.get(themeKey).then((stored) => { + applyTheme((stored[themeKey] as { theme?: string } | undefined)?.theme); + }); + + let state: ExtensionState | undefined; + let currentTab: ActiveTabInfo | undefined; + let frame: HTMLIFrameElement | undefined; + let expanded = false; + let idleTimer: ReturnType | undefined; + let drag: { pointerId: number; startX: number; startY: number; moved: boolean } | undefined; + + const setBridgeStatus = (status: BridgeStatus) => { + signal.className = `floating-panel__signal ${status.state}`; + }; + const siteAllowed = (next: ExtensionState) => { + const origin = location.origin; + if (next.floatingPanel.siteMode === 'allowlist') return next.floatingPanel.siteOrigins.includes(origin); + if (next.floatingPanel.siteMode === 'denylist') return !next.floatingPanel.siteOrigins.includes(origin); + return true; + }; + const adjustForEdgeConflict = () => { + if (host.style.display === 'none') return; + const x = state?.floatingPanel.side === 'left' ? 8 : innerWidth - 8; + const desiredY = (state?.floatingPanel.y || 0.46) * innerHeight; + const previous = host.style.visibility; + host.style.visibility = 'hidden'; + const behind = document.elementFromPoint(x, desiredY); + host.style.visibility = previous; + if (!behind) return; + const position = getComputedStyle(behind).position; + const bounds = behind.getBoundingClientRect(); + if (!['fixed', 'sticky'].includes(position) || bounds.width < 32 || bounds.height < 32) return; + const offset = desiredY < innerHeight / 2 ? bounds.bottom + 30 : bounds.top - 30; + panel.style.top = `${Math.min(Math.max(offset / innerHeight, 0.08), 0.92) * 100}%`; + }; + const applyState = (next: ExtensionState) => { + const previousHandoffId = state?.handoff?.state === 'waiting_for_user' ? state.handoff.id : undefined; + state = next; + const taskTargetsPage = Boolean( + next.activeGrant && next.activeGrant.expiresAt > Date.now() + && currentTab && next.activeGrant.targets.some((target) => target.tabId === currentTab!.id), + ); + const hasPageHandoff = Boolean( + next.handoff?.state === 'waiting_for_user' && next.handoff.target.tabId === currentTab?.id, + ); + const visible = next.floatingPanel.enabled && siteAllowed(next) + && (next.floatingPanel.displayMode === 'always' || taskTargetsPage || hasPageHandoff); + host.style.display = visible ? '' : 'none'; + panel.classList.toggle('floating-panel--left', next.floatingPanel.side === 'left'); + panel.classList.toggle('floating-panel--right', next.floatingPanel.side === 'right'); + panel.style.top = `${next.floatingPanel.y * 100}%`; + if (!visible) collapse(); + const nextHandoff = next.handoff?.state === 'waiting_for_user' && next.handoff.target.tabId === currentTab?.id + ? next.handoff + : undefined; + if (nextHandoff && nextHandoff.id !== previousHandoffId) expand(); + requestAnimationFrame(adjustForEdgeConflict); + }; + const ensureFrame = () => { + if (frame) return; + frame = document.createElement('iframe'); + frame.title = 'Yakit Browser Agent'; + frame.src = `${browser.runtime.getURL('/floating.html')}?tabId=${currentTab?.id || ''}`; + panel.prepend(frame); + }; + const unloadFrame = () => { + frame?.remove(); + frame = undefined; + }; + function collapse() { + expanded = false; + panel.classList.remove('is-expanded'); + launcher.setAttribute('aria-label', '展开 Yakit Browser Agent'); + if (idleTimer) globalThis.clearTimeout(idleTimer); + idleTimer = globalThis.setTimeout(unloadFrame, PANEL_IDLE_UNLOAD_MS); + } + const expand = () => { + if (idleTimer) globalThis.clearTimeout(idleTimer); + ensureFrame(); + expanded = true; + panel.classList.add('is-expanded'); + launcher.setAttribute('aria-label', '收起 Yakit Browser Agent'); + }; + + const [initialState, initialTab, initialBridge] = await Promise.all([ + send('state.get'), + send('tab.active').catch(() => undefined), + send('bridge.status'), + ]); + currentTab = initialTab; + applyState(initialState); + setBridgeStatus(initialBridge); + + launcher.addEventListener('pointerdown', (event) => { + if (event.button !== 0) return; + drag = { pointerId: event.pointerId, startX: event.clientX, startY: event.clientY, moved: false }; + launcher.setPointerCapture(event.pointerId); + }); + launcher.addEventListener('pointermove', (event) => { + if (!drag || drag.pointerId !== event.pointerId) return; + if (Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) > 4) drag.moved = true; + if (!drag.moved) return; + const side = event.clientX < innerWidth / 2 ? 'left' : 'right'; + const y = Math.min(Math.max(event.clientY / innerHeight, 0.08), 0.92); + panel.classList.toggle('floating-panel--left', side === 'left'); + panel.classList.toggle('floating-panel--right', side === 'right'); + panel.style.top = `${y * 100}%`; + }); + launcher.addEventListener('pointerup', (event) => { + if (!drag || drag.pointerId !== event.pointerId) return; + const moved = drag.moved; + drag = undefined; + if (moved) { + const side = event.clientX < innerWidth / 2 ? 'left' : 'right'; + const y = Math.min(Math.max(event.clientY / innerHeight, 0.08), 0.92); + void send('panel.update', { side, y }).then(applyState).catch(() => undefined); + } else if (expanded) collapse(); else expand(); + }); + + const onStorageChange = (changes: Record) => { + if (isStateStorageChange(changes)) void send('state.get').then(applyState).catch(() => undefined); + if (themeKey in changes) applyTheme((changes[themeKey] as { newValue?: { theme?: string } } | undefined)?.newValue?.theme); + }; + const onRuntimeMessage = (message: unknown) => { + const input = message as { action?: string; payload?: BridgeStatus }; + if (input?.action === 'bridge.status.changed' && input.payload) setBridgeStatus(input.payload); + }; + const onFrameMessage = (event: MessageEvent) => { + const data = event.data as { channel?: string; type?: string; height?: number }; + if (event.source !== frame?.contentWindow || data?.channel !== 'yakit-floating-host') return; + if (data.type === 'collapse') collapse(); + if (data.type === 'resize' && typeof data.height === 'number' && frame) { + frame.style.height = `${Math.min(Math.max(Math.ceil(data.height), 160), Math.min(480, innerHeight - 16))}px`; + } + }; + const onKeyDown = (event: KeyboardEvent) => { + if (!state?.floatingPanel.shortcutEnabled || !event.altKey || !event.shiftKey || event.code !== 'KeyY') return; + if (host.style.display === 'none') return; + event.preventDefault(); + if (expanded) collapse(); else expand(); + }; + const onFullscreenChange = () => { + if (state?.floatingPanel.autoCollapseFullscreen && document.fullscreenElement) collapse(); + }; + const onResize = () => requestAnimationFrame(adjustForEdgeConflict); + browser.storage.onChanged.addListener(onStorageChange); + browser.runtime.onMessage.addListener(onRuntimeMessage); + globalThis.addEventListener('message', onFrameMessage); + globalThis.addEventListener('keydown', onKeyDown, true); + document.addEventListener('fullscreenchange', onFullscreenChange); + globalThis.addEventListener('resize', onResize); + ctx.onInvalidated(() => { + if (idleTimer) globalThis.clearTimeout(idleTimer); + browser.storage.onChanged.removeListener(onStorageChange); + browser.runtime.onMessage.removeListener(onRuntimeMessage); + globalThis.removeEventListener('message', onFrameMessage); + globalThis.removeEventListener('keydown', onKeyDown, true); + document.removeEventListener('fullscreenchange', onFullscreenChange); + globalThis.removeEventListener('resize', onResize); + host.remove(); + }); + }, +}); diff --git a/src/entrypoints/agent.content/style.css b/src/entrypoints/agent.content/style.css new file mode 100644 index 0000000..9181841 --- /dev/null +++ b/src/entrypoints/agent.content/style.css @@ -0,0 +1,115 @@ +html, body { width: 100%; height: 100%; margin: 0; pointer-events: none; } + +.floating-panel { + position: fixed; + z-index: 2147483646; + width: 46px; + transform: translateY(-50%); + color: var(--foreground); + font-family: var(--font-sans); + font-size: var(--text-md); + letter-spacing: 0; + filter: drop-shadow(0 9px 20px rgba(20, 24, 28, .2)); + transition: width .18s ease; + pointer-events: auto; +} +.floating-panel--left { left: 0; } +.floating-panel--right { right: 0; } +.floating-panel.is-expanded { width: min(326px, calc(100vw - 8px)); } + +/* Header: follows theme surface, brand tile keeps the dark logo chip */ +.floating-panel__header { + height: 46px; + display: flex; + align-items: center; + overflow: hidden; + border: 1px solid var(--border-strong); + background: var(--surface); + color: var(--foreground); + user-select: none; + touch-action: none; +} +.floating-panel--left .floating-panel__header { border-left: 0; border-radius: 0 8px 8px 0; } +.floating-panel--right .floating-panel__header { flex-direction: row-reverse; border-right: 0; border-radius: 8px 0 0 8px; } +.floating-panel.is-expanded .floating-panel__header { border-radius: 8px 8px 0 0; } +.floating-panel__brand { position: relative; width: 44px; height: 44px; flex: 0 0 44px; padding: 0; display: grid; place-items: center; border: 0; background: transparent; } +.floating-panel__brand img { width: 42px; height: 42px; display: block; object-fit: contain; pointer-events: none; } +.floating-panel__signal { position: absolute; right: 5px; bottom: 5px; width: 7px; height: 7px; border: 1px solid var(--surface); border-radius: 50%; background: #90979e; } +.floating-panel__signal.connected { background: #45b77d; } +.floating-panel__signal.connecting { background: #e3a632; } +.floating-panel__signal.negotiating { background: #e3a632; } +.floating-panel__signal.error { background: #dc5e5e; } +.floating-panel__title { min-width: 0; flex: 1; display: grid; gap: 1px; padding: 0 10px; } +.floating-panel__title strong, .floating-panel__title span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.floating-panel__title strong { color: var(--foreground); font-size: var(--text-md); font-weight: 600; line-height: 17px; } +.floating-panel__title span { color: var(--muted); font-size: var(--text-sm); line-height: 15px; } +.floating-panel__grip { color: var(--muted); } +.floating-panel__header > svg:last-child { margin: 0 10px 0 4px; color: var(--muted); } + +.floating-panel__body { + overflow: hidden; + border: 1px solid var(--border-strong); + border-top: 0; + border-radius: 0 0 var(--radius-md) var(--radius-md); + background: var(--surface); + box-shadow: var(--shadow-md); +} +.floating-tabs { width: auto; height: 34px; margin: 8px 10px 0; padding: 3px; display: grid; grid-template-columns: repeat(3, 1fr); border: 0; border-radius: 10px; background: var(--surface-subtle); } +.floating-tabs .ui-tabs-trigger { min-width: 0; height: 28px; display: flex; align-items: center; justify-content: center; gap: 5px; border-radius: 8px; font-size: var(--text-sm); } +.floating-tab-content { min-height: 208px; padding: 10px; display: grid; align-content: start; gap: 10px; } +.floating-section-heading { height: 28px; display: flex; align-items: center; justify-content: space-between; color: var(--muted); font-size: var(--text-xs); font-weight: 650; letter-spacing: .04em; text-transform: uppercase; } + +.floating-option-list { max-height: 224px; overflow-y: auto; display: grid; gap: 4px; scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; } +.floating-option-list::-webkit-scrollbar { width: 8px; } +.floating-option-list::-webkit-scrollbar-track { background: transparent; } +.floating-option-list::-webkit-scrollbar-thumb { border-radius: 4px; background: var(--border-strong); } +.floating-option-list > button { width: 100%; min-height: 46px; padding: 6px 10px; display: flex; align-items: center; gap: 9px; border: 0; border-radius: var(--radius-md); background: transparent; color: var(--foreground); text-align: left; transition: background-color .13s ease; } +.floating-option-list > button:hover { background: var(--surface-subtle); } +.floating-option-list > button.is-active { background: var(--primary-soft); color: var(--primary-text); } +.floating-radio { width: 15px; height: 15px; flex: 0 0 auto; padding: 2.5px; border: 1.5px solid var(--border-strong); border-radius: 50%; background-clip: content-box; transition: border-color .13s ease; } +.floating-option-list > button.is-active .floating-radio { border-color: var(--primary); background-color: var(--primary); } +.floating-option-list strong, .floating-option-list small { display: block; } +.floating-option-list > button > span { min-width: 0; } +.floating-option-list strong { font-size: var(--text-md); font-weight: 600; line-height: 17px; } +.floating-option-list small { margin-top: 2px; color: var(--muted); font-size: var(--text-sm); line-height: 15px; } + +.floating-page-meta { min-width: 0; padding: 9px 12px; display: grid; gap: 3px; border-radius: var(--radius-md); background: var(--surface-subtle); } +.floating-page-meta strong, .floating-page-meta span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.floating-page-meta strong { font-size: var(--text-md); font-weight: 600; } +.floating-page-meta span { color: var(--muted); font-size: var(--text-sm); } + +.floating-result { min-height: 34px; padding: 4px 6px 4px 12px; display: flex; align-items: center; justify-content: space-between; gap: 8px; border-radius: var(--radius-md); background: var(--success-soft); color: var(--success); font-size: var(--text-sm); } + +.floating-status-row { min-height: 54px; padding: 8px 10px; display: grid; grid-template-columns: 8px 1fr auto; gap: 9px; align-items: center; border-radius: var(--radius-md); background: var(--surface-subtle); } +.floating-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border-strong); } +.floating-dot.connected { background: var(--success); } +.floating-dot.connecting { background: var(--warning); } +.floating-dot.negotiating { background: var(--warning); } +.floating-dot.error { background: var(--danger); } +.floating-status-row strong, .floating-status-row small { display: block; } +.floating-status-row strong { font-size: var(--text-md); font-weight: 600; } +.floating-status-row small { margin-top: 2px; color: var(--muted); font-size: var(--text-sm); } + +.floating-agent-task { min-height: 46px; padding: 8px 8px 8px 12px; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; border-radius: var(--radius-md); background: var(--success-soft); } +.floating-agent-task.paused, .floating-agent-task.waiting_for_human { background: var(--warning-soft); } +.floating-agent-task strong, .floating-agent-task small { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.floating-agent-task strong { color: var(--success); font-size: var(--text-md); font-weight: 600; } +.floating-agent-task.paused strong, .floating-agent-task.waiting_for_human strong { color: var(--warning); } +.floating-agent-task small { margin-top: 3px; color: var(--muted); font-size: var(--text-sm); } + +.floating-share-row { min-height: 54px; padding: 8px 10px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-radius: var(--radius-md); background: var(--surface-subtle); } +.floating-share-row strong, .floating-share-row small { display: block; } +.floating-share-row strong { font-size: var(--text-md); font-weight: 600; } +.floating-share-row small { margin-top: 3px; color: var(--muted); font-size: var(--text-sm); } + +.floating-handoff { min-height: 112px; padding: 12px; display: grid; align-content: space-between; gap: 12px; border: 1px solid color-mix(in srgb, var(--warning) 30%, var(--surface)); border-radius: var(--radius-md); background: var(--warning-soft); } +.floating-handoff__copy { min-width: 0; display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: 8px; align-items: start; } +.floating-handoff__copy > svg { margin-top: 1px; color: var(--warning); } +.floating-handoff__copy strong, .floating-handoff__copy small { display: block; } +.floating-handoff__copy strong { color: var(--warning); font-size: var(--text-md); font-weight: 600; line-height: 17px; } +.floating-handoff__copy small { margin-top: 4px; color: var(--muted); font-size: var(--text-sm); line-height: 16px; overflow-wrap: anywhere; } +.floating-handoff__actions { display: grid; grid-template-columns: 1fr auto; gap: 6px; } + +.floating-notice { margin: 0 10px 10px; padding: 8px 12px; border-radius: var(--radius-md); background: var(--danger-soft); color: var(--danger); font-size: var(--text-sm); line-height: 1.45; } +.spin { animation: floating-spin .8s linear infinite; } +@keyframes floating-spin { to { transform: rotate(360deg); } } diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index e407478..29a594c 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -1,73 +1,6 @@ -import { browser, type Browser } from 'wxt/browser'; -import {ContentActionType, ProxyActionType} from '@/types/action'; -import { getCurrentProxyMode, switchProxyMode } from '@/utils/proxy'; -import { getProxyConfig, saveProxyConfig } from '@/utils/storage'; -import type { ProxyConfig } from '@/types/proxy'; - -// 固定的代理模式配置 -const FIXED_MODES = [ - { - id: 'direct', - name: '[直接连接]', - proxyType: 'direct', - enabled: false - }, - { - id: 'system', - name: '[系统代理]', - proxyType: 'system', - enabled: false - } -]; +import { runBackground } from '@/app/background'; export default defineBackground({ type: 'module', - - async main() { - // 初始化固定模式的代理配置 - await initializeFixedModes(); - - // 初始化代理状态监听 - browser.runtime.onMessage.addListener((message: any, sender: Browser.runtime.MessageSender, sendResponse: (response?: any) => void) => { - if (message.action === ProxyActionType.GET_PROXY_STATUS) { - // 获取当前代理状态 - getCurrentProxyMode().then(mode => { - sendResponse({ success: true, data: { mode } }); - }); - return true; - } else if (message.action === ProxyActionType.SWITCH_PROXY) { - // 切换代理 - switchProxyMode(message.mode).then(success => { - sendResponse({ success }); - - // 如果切换成功,广播代理状态更改消息 - if (success) { - browser.runtime.sendMessage({ - action: ContentActionType.PROXY_CONFIGS_UPDATED, - source: 'background' - }); - } - }); - return true; - } - }); - - console.log('代理管理后台服务已启动'); - }, + main: runBackground, }); - -// 初始化固定模式的代理配置 -async function initializeFixedModes() { - try { - // 确保固定模式的配置已保存到数据库 - for (const modeConfig of FIXED_MODES) { - const existingConfig = await getProxyConfig(modeConfig.id); - if (!existingConfig) { - console.log(`初始化固定模式配置: ${modeConfig.id}`); - await saveProxyConfig(modeConfig as ProxyConfig); - } - } - } catch (error) { - console.error('初始化固定模式配置失败:', error); - } -} diff --git a/src/entrypoints/content.ts b/src/entrypoints/content.ts deleted file mode 100644 index 264a528..0000000 --- a/src/entrypoints/content.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default defineContentScript({ - matches: ['*://*.google.com/*'], - main() { - console.log('Hello content.'); - }, -}); diff --git a/src/entrypoints/floating/index.html b/src/entrypoints/floating/index.html new file mode 100644 index 0000000..cd3bbab --- /dev/null +++ b/src/entrypoints/floating/index.html @@ -0,0 +1,12 @@ + + + + + + Yakit Browser Agent + + +
+ + + diff --git a/src/entrypoints/floating/main.tsx b/src/entrypoints/floating/main.tsx new file mode 100644 index 0000000..4a827c4 --- /dev/null +++ b/src/entrypoints/floating/main.tsx @@ -0,0 +1,46 @@ +import { useEffect, useState } from 'react'; +import { createRoot } from 'react-dom/client'; +import { browser } from 'wxt/browser'; +import { TooltipProvider } from '@/components/ui/tooltip'; +import { FloatingPanel } from '@/features/floating-panel/FloatingPanel'; +import type { ActiveTabInfo, BridgeStatus, ExtensionState } from '@/types/models'; +import { request } from '@/platform/messaging/runtime'; +import { watchTheme } from '@/platform/storage/appearance'; +import '@/styles/global.css'; +import '../agent.content/style.css'; +import './style.css'; + +watchTheme(); + +function FloatingApp() { + const [initial, setInitial] = useState<{ state: ExtensionState; tab?: ActiveTabInfo; bridge: BridgeStatus }>(); + const [error, setError] = useState(''); + + useEffect(() => { + const tabId = Number(new URLSearchParams(location.search).get('tabId')); + void Promise.all([ + request('state.get'), + Number.isSafeInteger(tabId) && tabId > 0 + ? request('tab.get', { tabId }).catch(() => undefined) + : Promise.resolve(undefined), + request('bridge.status'), + ]).then(([state, tab, bridge]) => setInitial({ state, tab, bridge })) + .catch((reason) => setError(reason instanceof Error ? reason.message : String(reason))); + }, []); + + if (error) return
{error}
; + if (!initial) return
正在加载
; + return ( + + ); +} + +createRoot(document.getElementById('app')!).render( + , +); diff --git a/src/entrypoints/floating/style.css b/src/entrypoints/floating/style.css new file mode 100644 index 0000000..e8949ad --- /dev/null +++ b/src/entrypoints/floating/style.css @@ -0,0 +1,8 @@ +html, body, #app { width: 100%; height: 100%; min-width: 0; min-height: 0; margin: 0; overflow: hidden; } +body { background: transparent; } +.floating-panel.floating-panel--embedded { position: relative; inset: auto; width: 100%; height: 100%; transform: none; filter: none; } +.floating-panel--embedded .floating-panel__header { border-radius: 8px 8px 0 0; } +.floating-panel--embedded .floating-panel__body { max-height: calc(100% - 46px); overflow: auto; box-shadow: none; } +.floating-panel--embedded .floating-panel__brand { visibility: hidden; } +.floating-frame-loading, .floating-frame-error { height: 100%; display: grid; place-items: center; padding: 16px; box-sizing: border-box; color: var(--muted); background: var(--surface); font: 13px var(--font-sans); } +.floating-frame-error { color: var(--danger); } diff --git a/src/entrypoints/options/App.css b/src/entrypoints/options/App.css index 971ff8b..d878d05 100644 --- a/src/entrypoints/options/App.css +++ b/src/entrypoints/options/App.css @@ -1,85 +1,628 @@ -.options-layout { - min-height: 100vh; -} +/* Options 工作台 —— 基于 src/styles/tokens.css 令牌,暗色由 html[data-theme='dark'] 自动切换 */ -.options-header { - background-color: #F28B44; - display: flex; +code, pre { font-family: var(--font-mono); } +pre { margin: 0; } + +input[type='checkbox'] { width: 15px; height: 15px; flex: 0 0 auto; padding: 0; accent-color: var(--primary); } + +/* ---------- 原生按钮(组件库之外的 )} +
+ 外观 + +
+
{bridge.state === 'connected' ? '引擎在线' : '引擎离线'}{state.bridge.transport === 'native' ? state.bridge.nativeHost : state.bridge.endpoint}
+ + +
+
+
+ {tab?.favIconUrl ? : } + +
+
{state.activeGrant ? `${isControlScopeSet(state.activeGrant.scopes) ? '控制' : '只读'}会话` : '未共享'}
+
+ + {handoff && } + +
+ {section === 'overview' && } + {section === 'proxies' && } + {section === 'rules' && } + {section === 'cookies' && } + {section === 'user-agent' && } + {section === 'network' && } + {section === 'context' && } + {section === 'engine' && } + {section === 'activity' && } +
+ {notice &&
{notice.kind === 'ok' ? : }{notice.text}
} +
+ + ); +} + +function HandoffBanner({ handoff, setState, run, busy }: { handoff: HumanHandoff; setState: (state: ExtensionState) => void; run: (task: () => Promise, success?: string) => Promise; busy: boolean }) { + const resolve = (outcome: 'completed' | 'cancelled') => run( + async () => setState(await request('handoff.resolve', { id: handoff.id, outcome })), + outcome === 'completed' ? '已通知 Agent 继续执行' : '人工接管已取消', + ); + return
+ +
+ {HANDOFF_REASON_LABELS[handoff.reason]} + {handoff.message} + {handoff.target.title} · {handoff.target.origin} +
+
+ + +
+
; +} + +function ActivityLog({ run, busy }: { run: (task: () => Promise, success?: string) => Promise; busy: boolean }) { + const [events, setEvents] = useState([]); + const [runtime, setRuntime] = useState({ state: 'idle', updatedAt: Date.now(), actions: [] }); + const [loading, setLoading] = useState(true); + const [loadError, setLoadError] = useState(''); + const loadEvents = useCallback(async () => { try { - setLoading(true); - - const newProxy: Partial = { - id: uuidv4(), - name: values.name, - enabled: false, - }; - - if (values.proxyType === "fixed_servers") { - newProxy.proxyType = "fixed_servers"; - newProxy.scheme = values.scheme; - newProxy.host = values.host; - newProxy.port = Number(values.port); - - // 处理不经过代理的地址 - if (values.bypassList) { - newProxy.bypassList = values.bypassList - .split("\n") - .map((line: string) => line.trim()) - .filter((line: string) => line.length > 0); - } else { - newProxy.bypassList = []; - } - } else if (values.proxyType === "pac_script") { - newProxy.proxyType = "pac_script"; - newProxy.mode = "pac_script"; - - // 处理PAC脚本匹配域名 - if (values.matchList) { - newProxy.matchList = values.matchList - .split("\n") - .map((line: string) => line.trim()) - .filter((line: string) => line.length > 0); - } - - // 解析选择的代理服务器 - const [host, port] = values.proxyServer.split(":"); - - // 创建PAC脚本 - newProxy.pacScript = { - data: `function FindProxyForURL(url, host) { - // Convert host to lowercase for case-insensitive matching - host = host.toLowerCase(); - - // Define domain patterns - var domains = ${JSON.stringify(newProxy.matchList || [])}; - - // Check each domain pattern - for (var i = 0; i < domains.length; i++) { - var pattern = domains[i].toLowerCase(); - - if (pattern.startsWith('*.')) { - var suffix = pattern.substring(2); - if (host === suffix || host.endsWith('.' + suffix)) { - return 'PROXY ${host}:${port}'; - } - } else if (host === pattern) { - return 'PROXY ${host}:${port}'; - } - } - - return 'DIRECT'; -}`, - mandatory: true, - }; - - // 保存代理服务器信息 - newProxy.host = host; - newProxy.port = Number(port); - } - - // 添加认证信息 - if (values.username) newProxy.username = values.username; - if (values.password) newProxy.password = values.password; - - await saveProxyConfig(newProxy as ProxyConfig); - - // 重新加载代理列表 - await loadProxies(); - - // 重置表单 - form.resetFields(); - - // 关闭模态框 - setIsModalOpen(false); - - // 通知后台脚本 - browser.runtime.sendMessage({ - action: ContentActionType.PROXY_CONFIGS_UPDATED, - source: "options", - }); + setLoadError(''); + setEvents(await request('audit.list', { limit: 200 })); } catch (error) { - console.error("Error saving proxy:", error); + setLoadError(errorMessage(error)); } finally { setLoading(false); } - }; + }, []); + const loadRuntime = useCallback(() => request('agent.runtime.get').then(setRuntime), []); + useEffect(() => { + void Promise.all([loadEvents(), loadRuntime()]); + const listener = (changes: Record) => { + if (AUDIT_STORAGE_KEY in changes) void loadEvents(); + if (AGENT_RUNTIME_STORAGE_KEY in changes) void loadRuntime(); + }; + browser.storage.onChanged.addListener(listener); + return () => browser.storage.onChanged.removeListener(listener); + }, [loadEvents, loadRuntime]); - const handleDelete = async (id: string) => { - try { - await deleteProxyConfig(id); - await loadProxies(); + const runtimeLabel = { + idle: '无活动任务', running: 'Agent 运行中', paused: '已暂停', waiting_for_human: '等待用户', + revoked: '授权已撤销', expired: '授权已过期', + }[runtime.state]; + const downloadDiagnostics = () => run(async () => { + const bundle = await request('diagnostics.export'); + const url = URL.createObjectURL(new Blob([JSON.stringify(bundle, null, 2)], { type: 'application/json' })); + const link = document.createElement('a'); + link.href = url; + link.download = `yakit-browser-agent-diagnostics-${new Date().toISOString().replaceAll(':', '-')}.json`; + link.click(); + URL.revokeObjectURL(url); + }, '脱敏诊断包已导出'); - // 通知后台脚本 - browser.runtime.sendMessage({ - action: ContentActionType.PROXY_CONFIGS_UPDATED, - source: "options", - }); - } catch (error) { - console.error(`Error deleting proxy ${id}:`, error); - } - }; - - const handleActivate = async (id: string) => { - try { - // 启用代理 - await enableProxyConfig(id); - - // 发送切换代理请求 - await browser.runtime.sendMessage({ - action: ProxyActionType.SWITCH_PROXY, - mode: id, - }); - - // 重新加载代理列表 - await loadProxies(); - - // 通知后台脚本 - browser.runtime.sendMessage({ - action: ContentActionType.PROXY_CONFIGS_UPDATED, - source: "options", - }); - } catch (error) { - console.error(`Error activating proxy ${id}:`, error); - } - }; - - const showModal = () => { - form.resetFields(); - setIsModalOpen(true); - }; - - const handleCancel = () => { - form.resetFields(); - setIsModalOpen(false); - }; - - return ( - - -
- - Yaklang 代理管理设置 - -
- - } - onClick={showModal} - size="small" - style={{ - backgroundColor: "#F28B44", - borderColor: "#F28B44", - borderRadius: "4px", - fontSize: "13px", - }} - > - 添加代理 - - } - > - -
- No data -
-

还没有添加任何代理

- - ), - }} - renderItem={(proxy) => ( - handleActivate(proxy.id)} - disabled={proxy.enabled} - style={ - proxy.enabled - ? { - backgroundColor: "#F28B44", - color: "white", - borderColor: "#F28B44", - } - : undefined - } - > - {proxy.enabled ? "已启用" : "启用"} - , - , - , - ]} - > -
- 名称} - rules={[{ required: true, message: "请输入代理名称" }]} - > - - - - 类型} - initialValue="fixed_servers" - rules={[{ required: true, message: "请选择代理类型" }]} - > - - - - - prevValues.proxyType !== currentValues.proxyType - } - > - {({ getFieldValue }) => { - const proxyType = getFieldValue("proxyType"); - if (proxyType === "fixed_servers") { - return ( - <> - 协议} - initialValue="http" - rules={[ - { required: true, message: "请选择代理协议" }, - ]} - > - - - - 主机} - rules={[ - { required: true, message: "请输入主机地址" }, - ]} - > - - - - 端口} - rules={[{ required: true, message: "请输入端口" }]} - > - - - - - - -
- 每行一个地址,支持通配符 * -
- - - - - - - - - - - - ); - } else if (proxyType === "pac_script") { - return ( - <> - - - -
- 每行一个域名,支持通配符 * -
- - - 选择代理服务器 - - } - rules={[ - { required: true, message: "请选择代理服务器" }, - ]} - > - - - - ); - } - return null; - }} -
-
- -
-
-
- ); + return
+

Agent 操作时间线

实时动作保存在浏览器 session;长期审计只保存脱敏摘要,不记录参数、页面内容、Cookie 或执行结果。

{runtimeLabel}
+
+
当前任务{runtime.taskId || '未共享'}{runtime.grantId ? `Grant ${runtime.grantId.slice(0, 8)}` : '没有活动授权'}
最近更新{new Date(runtime.updatedAt).toLocaleTimeString()}{runtime.actions.length} 条 session 动作
{runtime.state === 'running' || runtime.state === 'waiting_for_human' ? : runtime.state === 'paused' ? : null}{runtime.grantId && !['revoked', 'expired'].includes(runtime.state) && }
+ {runtime.actions.length === 0 ?
当前 session 尚无 Agent 能力调用。
:
{[...runtime.actions].reverse().slice(0, 50).map((action) =>
{action.method}{action.targetTabId ? `Tab ${action.targetTabId}` : '扩展本机'}{action.state}{action.durationMs === undefined ? '进行中' : `${action.durationMs} ms`}
)}
} +
+

持久化脱敏审计

最近 500 条授权、Bridge、接管与能力结果。

+ {loading ?
正在读取记录
: loadError ?
{loadError}
: events.length === 0 ? 还没有操作记录。 :
+
时间类型动作目标 / 摘要结果耗时
+ {events.map((event) =>
+ + {AUDIT_CATEGORY_LABELS[event.category]} + {event.action} + {event.summary || (event.targetTabId ? `标签页 ${event.targetTabId}` : event.taskId ? `任务 ${event.taskId}` : '扩展本机')} + {AUDIT_OUTCOME_LABELS[event.outcome]} + {event.durationMs === undefined ? '—' : `${event.durationMs} ms`} +
)} +
} +
; } + +function Overview({ state, bridge, tab, navigate, run, busy }: { state: ExtensionState; bridge: BridgeStatus; tab?: ActiveTabInfo; navigate: (value: Section) => void; run: (task: () => Promise, success?: string) => Promise; busy: boolean }) { + const activeProxy = state.proxyProfiles.find((profile) => profile.id === state.activeProxyId)?.name || (state.activeProxyId === 'rules' ? '按规则分流' : '未知'); + const [runtime, setRuntime] = useState({ state: 'idle', updatedAt: Date.now(), actions: [] }); + const [network, setNetwork] = useState(); + const [loginContext, setLoginContext] = useState(); + useEffect(() => { + void request('agent.runtime.get').then(setRuntime).catch(() => undefined); + if (tab) void request('network.capture.status', { tabId: tab.id }).then(setNetwork).catch(() => setNetwork(undefined)); + const listener = (changes: Record) => { + if (AGENT_RUNTIME_STORAGE_KEY in changes) void request('agent.runtime.get').then(setRuntime).catch(() => undefined); + }; + browser.storage.onChanged.addListener(listener); + return () => browser.storage.onChanged.removeListener(listener); + }, [tab?.id]); + const site = tab?.url ? new URL(tab.url) : undefined; + const latestAction = [...runtime.actions].reverse()[0]; + const captureLoginEnvironment = () => run(async () => { + if (!tab) throw new Error('请先选择 HTTP(S) 标签页'); + setLoginContext(await request('context.capture', { + tabId: tab.id, includeDom: true, includeStorage: true, includeCookies: true, + })); + }, '登录环境已采集'); + const startCapture = () => run(async () => { + if (!tab) throw new Error('请先选择 HTTP(S) 标签页'); + setNetwork(await request('network.capture.start', { tabId: tab.id, captureHeaders: false, captureBody: false })); + navigate('network'); + }, '网络元数据捕获已启动'); + return
+

运行概览

{tab?.title || '选择一个 HTTP(S) 标签页,建立浏览器现场。'}

{bridge.state === 'connected' ? `Yak ${bridge.engineVersion || '引擎'} 在线` : 'Yak 引擎离线'}
+
+
{loginContext?.authentication.status === 'authenticated' ? '检测到登录环境' : loginContext?.authentication.status === 'unauthenticated' ? '未检测到登录态' : '登录环境待采集'}{site ? `${site.protocol.replace(':', '').toUpperCase()} · ${site.origin}` : '当前页面不可访问'}
+
+
+
+
浏览器现场{loginContext ? `${loginContext.document?.forms.length || 0} 表单 / ${loginContext.document?.interactive.length || 0} 节点` : '尚未采集'}{loginContext?.authentication.evidence[0] || 'Cookie、Storage 与认证信号仅在用户点击后读取'}
+
代理与流量{activeProxy}{network?.active ? `${network.count} 条请求,${network.droppedCount} 条丢弃` : `${state.proxyRules.filter((rule) => rule.enabled).length} 条分流规则 · 捕获未启动`}
+
Agent 会话{state.activeGrant ? `${isControlScopeSet(state.activeGrant.scopes) ? '控制' : '只读'} · ${runtime.state}` : '未共享'}{state.activeGrant ? `${state.activeGrant.targets.length} 个 frame · ${new Date(state.activeGrant.expiresAt).toLocaleTimeString()} 到期` : '创建 task-bound grant 后才允许远程读取'}
+
需要用户处理{state.handoff?.state === 'waiting_for_user' ? HANDOFF_REASON_LABELS[state.handoff.reason] : runtime.state === 'paused' ? 'Agent 已暂停' : '没有待办步骤'}{state.handoff?.state === 'waiting_for_user' ? state.handoff.message : latestAction ? `最近 ${latestAction.method} · ${latestAction.state}` : '二维码、MFA 与 CAPTCHA 会在这里出现'}
+
+
+ + + +
+
; +} + +function ProxyProfiles({ state, setState, run, busy }: { state: ExtensionState; setState: (state: ExtensionState) => void; run: (task: () => Promise, success?: string) => Promise; busy: boolean }) { + const empty: ProxyProfile = { id: '', name: '', kind: 'fixed_servers', scheme: 'http', host: '127.0.0.1', port: 8083, bypass: ['localhost', '127.0.0.1', ''] }; + const [draft, setDraft] = useState(); + const [authPassword, setAuthPassword] = useState(''); + const [authConfigured, setAuthConfigured] = useState(false); + const selectDraft = (profile: ProxyProfile) => { + setDraft(profile); + setAuthPassword(''); + void request('proxy.auth.status', { profileId: profile.id }).then((result) => setAuthConfigured(result.configured)); + }; + const saveProfile = () => run(async () => { + if (!draft) return; + setState(await request('proxy.save', draft)); + if (draft.authEnabled && authPassword) { + const result = await request('proxy.auth.set', { profileId: draft.id, password: authPassword }); + setAuthConfigured(result.configured); + setAuthPassword(''); + } else if (!draft.authEnabled) { + await request('proxy.auth.set', { profileId: draft.id, password: '' }); + setAuthConfigured(false); + } + }, '代理配置已保存'); + return
+
+

代理配置

固定代理、SOCKS、PAC 与会话级认证出口。

+
{state.proxyProfiles.map((profile) => )}
+
+
{draft ? <> +

{draft.builtin ? '内置代理' : '编辑代理'}

{draft.id}

+
+ setDraft({ ...draft, name: event.target.value })} /> + + {draft.kind === 'fixed_servers' && <> setDraft({ ...draft, host: event.target.value })} /> setDraft({ ...draft, port: Number(event.target.value) })} />