- commit
- 7d4f292
- parent
- daf1574
- author
- im_wower
- date
- 2026-03-22 11:41:27 +0800 CST
Vendor baa-firefox into plugin subdirectory
19 files changed,
+7710,
-4
+1,
-1
1@@ -2604,7 +2604,7 @@ Scope:
2 - Firefox 插件集成文档
3 - 浏览器状态协议
4
5-这部分可以落在独立仓库里,但协议应该在这里先定义清楚。
6+这部分现在统一收口在本仓库的 Firefox 插件子目录里,但协议仍然在这里定义清楚。
7
8 ## 35.10 Task J: Status API 与基础 UI
9
+2,
-0
1@@ -27,6 +27,8 @@ apps/
2 conductor-daemon/
3 status-api/
4 worker-runner/
5+plugins/
6+ baa-firefox/
7 packages/
8 auth/
9 checkpointing/
+1,
-1
1@@ -40,4 +40,4 @@ npx --yes pnpm install
2 - 当前仍然采用“内网走 Tailscale `100.x`,外网走二级域名”
3 - 当前仍不依赖 MagicDNS 名称
4 - `README.md`、`DESIGN.md`、根配置文件仍视为热点文件,非任务必要不要碰
5-- `T-026` 是跨仓库任务,目标仓库为 `/Users/george/code/baa/baa-firefox`
6+- `T-026` 在当时是跨仓库任务;当前 Firefox 插件代码已经收口到本仓库子目录 `plugins/baa-firefox/`
+1,
-1
1@@ -62,7 +62,7 @@
2 - `README.md`、`DESIGN.md`、根配置文件应避免多人同时修改
3 - `ops/launchd/**` 已合入,当前主线已经产出 app 级 `dist/index.js`
4 - `apps/conductor-daemon/package.json` 与 `apps/status-api/package.json` 的构建脚本已在第三波整合时收口
5-- `baa-firefox` 的 `T-026` 已单独合入其仓库 `main`
6+- Firefox 插件代码现已收口到本仓库子目录 `plugins/baa-firefox/`
7
8 ## 后续汇总规则
9
+6,
-1
1@@ -1,5 +1,11 @@
2 # Firefox Control Protocol
3
4+当前 Firefox 插件实现代码已经直接收口到本仓库子目录:
5+
6+- [`../../plugins/baa-firefox/`](../../plugins/baa-firefox/)
7+
8+这里保留协议和控制面约定文档;具体实现以 `plugins/baa-firefox/` 为准。
9+
10 本文档定义 `baa-firefox` 与 `baa-conductor` control API 之间的最小协议。
11
12 目标:
13@@ -10,7 +16,6 @@
14
15 非目标:
16
17-- 不在本仓库实现 Firefox 插件代码
18 - 不定义完整 UI 视觉稿
19 - 不让浏览器成为调度真相来源
20
+2,
-0
1@@ -0,0 +1,2 @@
2+node_modules/
3+.DS_Store
+436,
-0
1@@ -0,0 +1,436 @@
2+# BAA Firefox MVP Plan
3+
4+## Goal
5+
6+Build the smallest Firefox extension MVP that proves this model works:
7+
8+- one always-open controller page inside the extension
9+- one real top-level AI tab
10+- manual login only
11+- automatic discovery of API endpoints, auth headers, and streaming events
12+- a long-lived WebSocket from the controller page to `baa-server`
13+- no DOM parsing as the primary path
14+
15+The first platform should be `claude.ai` only. After it works, generalize.
16+
17+## Non-Goals
18+
19+Do not solve these in MVP:
20+
21+- multi-platform support
22+- multiple tabs per platform
23+- multiple accounts
24+- multi-window coordination
25+- iframe embedding
26+- full request execution from server into the AI tab
27+- automatic login
28+- deep DOM automation
29+- generic worker/websocket interception
30+- polished UI
31+
32+## Core Decision
33+
34+Do not keep the main WebSocket in `background.js`.
35+
36+Instead:
37+
38+- `controller.html` stays open in a normal browser tab
39+- `controller.js` owns the WebSocket and the runtime state
40+- `background.js` becomes thin bootstrap and helper logic only
41+
42+Reason:
43+
44+- Firefox MV3 background lifetime is the main risk
45+- a visible extension page is simpler than trying to keep a background context alive
46+- the controller page can directly use WebExtension APIs needed for MVP
47+
48+## MVP Architecture
49+
50+### 1. Controller Page
51+
52+One extension page acts as the control tower.
53+
54+Responsibilities:
55+
56+- open and keep track of the single AI tab
57+- connect to `baa-server` over WS
58+- receive browser-side events from content scripts
59+- persist minimal state to `storage.local`
60+- surface simple status: connected, tab exists, credentials seen, endpoints count
61+
62+The controller page should be opened manually at first.
63+
64+Later, background can auto-open it on startup if needed.
65+
66+### 2. Real AI Tab
67+
68+Use one real top-level tab:
69+
70+- `https://claude.ai`
71+
72+Requirements:
73+
74+- user logs in manually
75+- no specific chat URL required
76+- the root app page is enough
77+
78+The tab is the real browser surface that carries:
79+
80+- cookies
81+- session
82+- same-origin rules
83+- anti-bot browser context
84+
85+### 3. Content Script Bridge
86+
87+Inject a lightweight content script into the AI tab.
88+
89+Responsibilities:
90+
91+- receive `CustomEvent`s from the page context
92+- forward them to the controller page or background
93+
94+This is the bridge between:
95+
96+- privileged extension code
97+- page MAIN world instrumentation
98+
99+### 4. MAIN World Interceptor
100+
101+Inject a MAIN world script into the AI tab.
102+
103+Responsibilities:
104+
105+- patch `window.fetch`
106+- observe request URL, method, headers, body
107+- observe response status, headers, body
108+- observe SSE chunks if present
109+- emit normalized events through `CustomEvent`
110+
111+This is the primary discovery layer for:
112+
113+- API endpoints
114+- request/response shapes
115+- streaming behavior
116+
117+### 5. Browser-Level Request Observer
118+
119+Use `webRequest` in parallel.
120+
121+Responsibilities:
122+
123+- capture outgoing request headers at browser level
124+- recover auth-related headers not visible from page JS
125+- build a credential snapshot
126+- provide a second signal for endpoint discovery
127+
128+This is the primary source for:
129+
130+- cookies
131+- CSRF-style headers
132+- browser-visible request metadata
133+
134+## Why This MVP Should Work
135+
136+This design avoids the fragile path:
137+
138+- no DOM scraping as the primary integration
139+- no button clicking or selector dependency
140+- no iframe
141+
142+This design keeps the stable path:
143+
144+- real browser tab
145+- real cookies
146+- real request flow
147+- extension-side network observation
148+
149+The working proof for MVP is simple:
150+
151+- the user logs into Claude manually
152+- the extension sees live endpoint traffic
153+- the extension captures auth material
154+- the extension sends these to `baa-server`
155+
156+## Files To Create
157+
158+The repo should start with these files:
159+
160+- `manifest.json`
161+- `controller.html`
162+- `controller.js`
163+- `background.js`
164+- `content-script.js`
165+- `page-interceptor.js`
166+- `README.md`
167+
168+Optional:
169+
170+- `controller.css`
171+
172+## File Responsibilities
173+
174+### `manifest.json`
175+
176+Firefox-targeted MV3 manifest.
177+
178+Include only what MVP needs:
179+
180+- `tabs`
181+- `storage`
182+- `webRequest`
183+- `cookies`
184+- `scripting`
185+- host permissions for `https://claude.ai/*`
186+
187+Register:
188+
189+- `background.js`
190+- `content-script.js`
191+- `page-interceptor.js`
192+- browser action or direct tab page for `controller.html`
193+
194+### `controller.html`
195+
196+Minimal UI only.
197+
198+Must show:
199+
200+- WS connected / disconnected
201+- AI tab exists / missing
202+- credentials seen / missing
203+- number of discovered endpoints
204+- recent log lines
205+
206+One button is enough:
207+
208+- `Open Claude Tab`
209+
210+### `controller.js`
211+
212+Main runtime owner.
213+
214+Responsibilities:
215+
216+- connect WS to `baa-server`
217+- create or recover the Claude tab
218+- maintain `platformTabId`
219+- receive forwarded events
220+- normalize and send them to server
221+- persist last known state
222+
223+State shape can be minimal:
224+
225+```js
226+{
227+ platform: "claude",
228+ tabId: 123,
229+ wsConnected: true,
230+ lastCredentialAt: 0,
231+ endpoints: {},
232+ lastHeaders: {},
233+ lastEvents: []
234+}
235+```
236+
237+### `background.js`
238+
239+Thin only.
240+
241+Responsibilities:
242+
243+- open/focus `controller.html` when extension icon is clicked
244+- optionally relay messages if direct page-to-tab wiring is awkward
245+- nothing long-lived beyond bootstrap
246+
247+No main WS should live here in MVP.
248+
249+### `content-script.js`
250+
251+Bridge only.
252+
253+Responsibilities:
254+
255+- listen for `CustomEvent`s from page context
256+- forward to extension runtime
257+
258+Keep it small.
259+
260+### `page-interceptor.js`
261+
262+MAIN world network observer.
263+
264+Responsibilities:
265+
266+- wrap `fetch`
267+- capture requests
268+- capture error responses
269+- capture streaming events where possible
270+
271+For MVP, patching `fetch` is enough.
272+
273+If Claude moves important traffic elsewhere later, expand after proof.
274+
275+## Message Flow
276+
277+### Browser Startup
278+
279+1. User opens `controller.html`
280+2. `controller.js` connects WS to `baa-server`
281+3. `controller.js` checks for existing Claude tab
282+4. If missing, it opens `https://claude.ai`
283+5. Content script and MAIN world interceptor are active on that tab
284+
285+### Manual Login
286+
287+1. User logs into Claude in the real tab
288+2. Claude app bootstraps and starts making network requests
289+3. `webRequest` captures browser-visible auth headers and cookies
290+4. MAIN world interceptor captures request and response data
291+5. Controller receives both and sends normalized events to `baa-server`
292+
293+### Endpoint Discovery
294+
295+1. MAIN world interceptor sees a request
296+2. URL path is normalized
297+3. Method + normalized path are added to endpoint registry
298+4. Controller sends discovered endpoints to `baa-server`
299+
300+### Credential Snapshot
301+
302+1. `webRequest` sees request headers
303+2. Controller extracts a reduced auth snapshot
304+3. Snapshot is timestamped and stored
305+4. Snapshot is sent to `baa-server`
306+
307+## Data Sent To Server
308+
309+MVP should send only these message types:
310+
311+- `hello`
312+- `endpoint_discovered`
313+- `credential_snapshot`
314+- `network_event`
315+- `sse_event`
316+- `status`
317+
318+Example normalized messages:
319+
320+```json
321+{
322+ "type": "hello",
323+ "clientId": "ff-xxxxxx",
324+ "nodeType": "browser",
325+ "nodeCategory": "proxy",
326+ "nodePlatform": "firefox"
327+}
328+```
329+
330+```json
331+{
332+ "type": "endpoint_discovered",
333+ "platform": "claude",
334+ "method": "POST",
335+ "path": "/api/organizations/{id}/messages"
336+}
337+```
338+
339+```json
340+{
341+ "type": "credential_snapshot",
342+ "platform": "claude",
343+ "headers": {
344+ "cookie": "...",
345+ "x-csrf-token": "...",
346+ "anthropic-client-sha": "..."
347+ },
348+ "ts": 0
349+}
350+```
351+
352+## Minimal Acceptance Criteria
353+
354+MVP is done when all of these are true:
355+
356+- opening `controller.html` establishes a WS connection to `baa-server`
357+- the controller can open or recover exactly one Claude tab
358+- manual login in that tab leads to captured credentials
359+- at least one live Claude API endpoint is discovered automatically
360+- request/response metadata is visible in server logs
361+- SSE chunks are visible if Claude uses streaming on the observed path
362+- closing the Claude tab and reopening it still restores the flow
363+
364+## Build Order
365+
366+Implement in this order:
367+
368+1. `manifest.json`
369+2. `controller.html` + `controller.js`
370+3. thin `background.js`
371+4. `content-script.js`
372+5. `page-interceptor.js`
373+6. `webRequest` credential capture
374+7. WS message normalization to server
375+8. minimal status UI
376+
377+## Scope Cuts If Time Is Tight
378+
379+If the first pass needs to be even smaller, cut these first:
380+
381+- pretty UI
382+- SSE chunk parsing details
383+- background auto-open behavior
384+- storage persistence beyond `tabId` and last headers
385+- endpoint normalization beyond very basic ID replacement
386+
387+Do not cut these:
388+
389+- controller-owned WS
390+- real top-level Claude tab
391+- MAIN world fetch interception
392+- `webRequest` credential capture
393+
394+## Deferred Work After MVP
395+
396+Only after the MVP works:
397+
398+- add active proxy execution from server into the live AI tab
399+- support ChatGPT and Gemini
400+- support multiple windows
401+- support multi-account contexts
402+- add stronger recovery logic
403+- add XHR and worker interception
404+- make the controller page optional by moving some logic back into a more robust runtime model
405+
406+## TODO: SSE 抓取
407+
408+Observed in the current Firefox MVP:
409+
410+- request / credential / endpoint capture works
411+- `POST /api/organizations/{id}/chat_conversations/{id}/completion` is discovered
412+- the current SSE bridge can still report `The operation was aborted` with `0` chunks
413+- this happened in the anonymous `https://claude.ai/new?incognito` flow after a real prompt/response round trip
414+
415+Next work should focus on these items:
416+
417+- reproduce the failure deterministically on both anonymous and logged-in organization chats
418+- verify whether `response.clone().body.getReader()` is racing with Claude's own stream consumption in Firefox
419+- test replacing the current clone-reader approach with a `ReadableStream.tee()` based interception path
420+- keep partial chunk state across boundaries so incomplete `data:` frames are not dropped
421+- include stable conversation identifiers in `sse_event` payloads when the completion URL contains them
422+- distinguish clean stream completion from abort / navigation / page refresh in emitted events
423+- add controller-side logs for first chunk, last chunk, abort reason, and total chunk count
424+- only mark SSE capture as done after a verified chunked response is observed in `baa-server` logs
425+
426+## Final Recommendation
427+
428+Do not over-design beyond this.
429+
430+The first goal is only to prove:
431+
432+- Firefox can keep a controller page open
433+- the controller page can keep a WS open
434+- a real Claude tab can be observed without DOM parsing
435+- endpoints and credentials can be learned and sent to `baa-server`
436+
437+Once that proof exists, the rest of the system can be evolved safely.
+114,
-0
1@@ -0,0 +1,114 @@
2+# BAA Firefox
3+
4+这个目录现在作为 `baa-conductor` 内部插件子目录维护。
5+
6+来源说明:
7+
8+- 初始代码来自原独立仓库 `baa-firefox`
9+- 已迁入当前仓库,后续以这里为主写入面
10+
11+Firefox MVP for the BAA browser-proxy path.
12+
13+## What This Repo Does
14+
15+This extension keeps an always-open controller page and one real browser tab per platform.
16+
17+It does four things:
18+
19+- keeps a WebSocket connection to `baa-server`
20+- opens or reuses real tabs for `claude.ai`, `chatgpt.com`, and `gemini.google.com`
21+- discovers live API endpoints from real browser traffic
22+- captures auth-related request headers and forwards them to `baa-server`
23+
24+This MVP is intentionally narrow:
25+
26+- manual login only
27+- one tracked tab per platform
28+- no iframe
29+- no DOM automation
30+- no full remote execution path yet
31+
32+## Files
33+
34+- `manifest.json` - Firefox MV3 manifest
35+- `background.js` - opens and keeps the controller tab around
36+- `controller.html` - always-open management page
37+- `controller.js` - WS owner, multi-platform tab manager, webRequest capture, message relay
38+- `content-script.js` - bridge from page events into the extension runtime
39+- `page-interceptor.js` - MAIN world `fetch` interceptor
40+- `scripts/run-persistent.sh` - starts Firefox with the persistent `baa-firefox-persistent` profile
41+- `PLAN.md` - MVP design notes
42+
43+## Persistent Startup
44+
45+If you already created and logged into the persistent Firefox profile once, start the extension with:
46+
47+```bash
48+./scripts/run-persistent.sh
49+```
50+
51+This launches Firefox with the `baa-firefox-persistent` profile, keeps profile changes, reuses the saved browser login state on this machine, and reloads already-open platform pages so the interceptor is injected immediately.
52+
53+Useful overrides:
54+
55+- `BAA_FIREFOX_PROFILE` - change the Firefox profile name
56+- `BAA_FIREFOX_BIN` - change the Firefox binary path
57+- `BAA_WEB_EXT_BIN` - change the `web-ext` executable
58+
59+Example:
60+
61+```bash
62+BAA_FIREFOX_PROFILE=baa-firefox-persistent ./scripts/run-persistent.sh
63+```
64+
65+## How To Load
66+
67+1. Open Firefox.
68+2. Visit `about:debugging#/runtime/this-firefox`.
69+3. Click `Load Temporary Add-on...`.
70+4. Choose [manifest.json](/Users/george/code/baa-conductor-main-merge/plugins/baa-firefox/manifest.json).
71+5. Firefox should open `controller.html` automatically.
72+
73+## How To Run
74+
75+1. Start `baa-server` so `ws://localhost:9800` is available.
76+2. Load this extension temporarily.
77+3. In the controller page, keep the default WS URL or change it.
78+4. Let the extension open `https://claude.ai/`, `https://chatgpt.com/`, and `https://gemini.google.com/`.
79+5. Log in manually in the tabs you want to intercept.
80+6. Use Claude, ChatGPT, or Gemini normally.
81+7. Watch the controller page and `baa-server` logs for:
82+ - endpoint discovery
83+ - credential snapshots
84+ - network events
85+ - SSE events
86+
87+## Expected MVP Result
88+
89+Once the platform tabs start making real requests, the extension should forward:
90+
91+- `hello`
92+- `api_endpoints`
93+- `credentials`
94+- `network_log`
95+- `sse_event`
96+- `client_log`
97+
98+These message shapes match the current `baa-server` browser-side WS handling.
99+
100+## Limitations
101+
102+- only one tab per platform is tracked
103+- only `fetch` is patched in the page
104+- there is no remote request execution path yet
105+- if Firefox unloads the extension, the session must be re-established
106+- Gemini interception is heuristic because its web app mixes private RPC paths under `/_/`
107+
108+## Next Step
109+
110+After this works end to end, add:
111+
112+- request execution into the live platform tabs
113+- stronger tab recovery
114+- broader network interception
115+- more robust Gemini and SSE coverage
+130,
-0
1@@ -0,0 +1,130 @@
2+const CONTROLLER_URL = browser.runtime.getURL("controller.html");
3+const STORAGE_KEY = "baaFirefox.controllerTabId";
4+const CONTROL_STATE_KEY = "baaFirefox.controlState";
5+const MODE_BADGES = {
6+ running: { text: "运行", color: "#1f6f5f" },
7+ paused: { text: "暂停", color: "#a6512f" },
8+ draining: { text: "排空", color: "#8b5e34" },
9+ unknown: { text: "--", color: "#7f7a70" }
10+};
11+
12+async function setControllerTabId(tabId) {
13+ await browser.storage.local.set({ [STORAGE_KEY]: tabId });
14+}
15+
16+async function getStoredControllerTabId() {
17+ const data = await browser.storage.local.get(STORAGE_KEY);
18+ return data[STORAGE_KEY] || null;
19+}
20+
21+async function queryControllerTab() {
22+ const tabs = await browser.tabs.query({ url: CONTROLLER_URL });
23+ return tabs[0] || null;
24+}
25+
26+async function ensureControllerTab(options = {}) {
27+ const { activate = false } = options;
28+
29+ let tab = await queryControllerTab();
30+ if (!tab) {
31+ const storedId = await getStoredControllerTabId();
32+ if (storedId) {
33+ try {
34+ tab = await browser.tabs.get(storedId);
35+ } catch (_) {
36+ tab = null;
37+ }
38+ }
39+ }
40+
41+ if (tab) {
42+ await setControllerTabId(tab.id);
43+ if (activate) {
44+ await browser.tabs.update(tab.id, { active: true });
45+ if (tab.windowId != null) {
46+ await browser.windows.update(tab.windowId, { focused: true });
47+ }
48+ }
49+ return tab;
50+ }
51+
52+ const created = await browser.tabs.create({
53+ url: CONTROLLER_URL,
54+ active: activate
55+ });
56+ await setControllerTabId(created.id);
57+ return created;
58+}
59+
60+function normalizeMode(value) {
61+ const lower = String(value || "").trim().toLowerCase();
62+ if (lower === "running" || lower === "paused" || lower === "draining") {
63+ return lower;
64+ }
65+ return "unknown";
66+}
67+
68+async function updateActionBadge(snapshot) {
69+ const mode = normalizeMode(snapshot?.mode);
70+ const badge = snapshot?.error ? { text: "ERR", color: "#a6512f" } : MODE_BADGES[mode];
71+ const modeLabel = mode === "running" ? "运行中" : mode === "paused" ? "已暂停" : mode === "draining" ? "排空中" : "未知";
72+ const leader = snapshot?.leader ? `\n主控: ${snapshot.leader}` : "";
73+
74+ await browser.action.setBadgeText({ text: badge.text });
75+ await browser.action.setBadgeBackgroundColor({ color: badge.color });
76+ await browser.action.setTitle({
77+ title: `BAA Firefox 管理页\n模式: ${modeLabel}${leader}`
78+ });
79+}
80+
81+async function refreshActionBadgeFromStorage() {
82+ const data = await browser.storage.local.get(CONTROL_STATE_KEY);
83+ await updateActionBadge(data[CONTROL_STATE_KEY] || null);
84+}
85+
86+browser.runtime.onInstalled.addListener(() => {
87+ ensureControllerTab({ activate: true }).catch(() => {});
88+ refreshActionBadgeFromStorage().catch(() => {});
89+});
90+
91+browser.runtime.onStartup.addListener(() => {
92+ ensureControllerTab({ activate: false }).catch(() => {});
93+ refreshActionBadgeFromStorage().catch(() => {});
94+});
95+
96+browser.action.onClicked.addListener(() => {
97+ ensureControllerTab({ activate: true }).catch(() => {});
98+});
99+
100+browser.runtime.onMessage.addListener((message) => {
101+ if (!message || typeof message !== "object") return undefined;
102+
103+ if (message.type === "controller_ready" && Number.isInteger(message.tabId)) {
104+ return setControllerTabId(message.tabId).then(() => ({ ok: true }));
105+ }
106+
107+ if (message.type === "focus_controller") {
108+ return ensureControllerTab({ activate: true }).then((tab) => ({
109+ ok: true,
110+ tabId: tab.id
111+ }));
112+ }
113+
114+ return undefined;
115+});
116+
117+browser.storage.onChanged.addListener((changes, areaName) => {
118+ if (areaName !== "local" || !changes[CONTROL_STATE_KEY]) return;
119+ updateActionBadge(changes[CONTROL_STATE_KEY].newValue || null).catch(() => {});
120+});
121+
122+browser.tabs.onRemoved.addListener((tabId) => {
123+ getStoredControllerTabId().then((storedId) => {
124+ if (storedId !== tabId) return;
125+ setTimeout(() => {
126+ ensureControllerTab({ activate: false }).catch(() => {});
127+ }, 300);
128+ }).catch(() => {});
129+});
130+
131+refreshActionBadgeFromStorage().catch(() => {});
+358,
-0
1@@ -0,0 +1,358 @@
2+const CONTROL_STATE_KEY = "baaFirefox.controlState";
3+const CLAUDE_ENTRY_HOSTS = ["claude.ai"];
4+
5+const claudeEntryState = {
6+ host: null,
7+ shadow: null,
8+ snapshot: null,
9+ expanded: false,
10+ busyAction: null,
11+ error: ""
12+};
13+
14+function sendBridgeMessage(type, data) {
15+ browser.runtime.sendMessage({
16+ type,
17+ data
18+ }).catch(() => {});
19+}
20+
21+function isRecord(value) {
22+ return !!value && typeof value === "object" && !Array.isArray(value);
23+}
24+
25+function isClaudePage() {
26+ return CLAUDE_ENTRY_HOSTS.some((host) => location.hostname === host || location.hostname.endsWith(`.${host}`));
27+}
28+
29+function normalizeMode(value) {
30+ const lower = String(value || "").trim().toLowerCase();
31+ if (lower === "running" || lower === "paused" || lower === "draining") {
32+ return lower;
33+ }
34+ return "unknown";
35+}
36+
37+function formatModeLabel(mode) {
38+ switch (normalizeMode(mode)) {
39+ case "running":
40+ return "运行中";
41+ case "paused":
42+ return "已暂停";
43+ case "draining":
44+ return "排空中";
45+ default:
46+ return "未知";
47+ }
48+}
49+
50+function normalizeEntrySnapshot(value) {
51+ if (!isRecord(value)) {
52+ return {
53+ mode: "unknown",
54+ leader: null,
55+ queueDepth: null,
56+ activeRuns: null,
57+ error: "未同步"
58+ };
59+ }
60+
61+ return {
62+ mode: normalizeMode(value.mode),
63+ leader: typeof value.leader === "string" && value.leader.trim() ? value.leader.trim() : null,
64+ queueDepth: Number.isFinite(value.queueDepth) ? Number(value.queueDepth) : null,
65+ activeRuns: Number.isFinite(value.activeRuns) ? Number(value.activeRuns) : null,
66+ error: typeof value.error === "string" && value.error ? value.error : ""
67+ };
68+}
69+
70+function ensureClaudeEntry() {
71+ if (!isClaudePage() || claudeEntryState.host || !document.documentElement) return;
72+
73+ const host = document.createElement("div");
74+ host.style.position = "fixed";
75+ host.style.right = "16px";
76+ host.style.bottom = "16px";
77+ host.style.zIndex = "2147483647";
78+
79+ const shadow = host.attachShadow({ mode: "open" });
80+ shadow.innerHTML = `
81+ <style>
82+ :host {
83+ all: initial;
84+ }
85+
86+ .wrap {
87+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
88+ color: #f5efe2;
89+ }
90+
91+ button {
92+ font: inherit;
93+ border: 0;
94+ cursor: pointer;
95+ }
96+
97+ .toggle {
98+ display: inline-flex;
99+ align-items: center;
100+ gap: 8px;
101+ padding: 9px 12px;
102+ border-radius: 999px;
103+ background: rgba(19, 26, 26, 0.92);
104+ color: inherit;
105+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
106+ }
107+
108+ .toggle strong {
109+ font-size: 12px;
110+ letter-spacing: 0.08em;
111+ text-transform: uppercase;
112+ }
113+
114+ .badge {
115+ padding: 4px 8px;
116+ border-radius: 999px;
117+ font-size: 11px;
118+ line-height: 1;
119+ }
120+
121+ .badge.running {
122+ background: rgba(31, 111, 95, 0.28);
123+ color: #91dbc7;
124+ }
125+
126+ .badge.paused,
127+ .badge.draining {
128+ background: rgba(166, 81, 47, 0.28);
129+ color: #ffd3bf;
130+ }
131+
132+ .badge.unknown {
133+ background: rgba(127, 122, 112, 0.28);
134+ color: #e5dccd;
135+ }
136+
137+ .panel {
138+ margin-top: 10px;
139+ width: 250px;
140+ padding: 12px;
141+ border-radius: 18px;
142+ background: rgba(19, 26, 26, 0.94);
143+ box-shadow: 0 16px 40px rgba(0, 0, 0, 0.24);
144+ }
145+
146+ .hidden {
147+ display: none;
148+ }
149+
150+ .topline,
151+ .meta,
152+ .actions {
153+ display: flex;
154+ align-items: center;
155+ gap: 8px;
156+ }
157+
158+ .topline,
159+ .meta {
160+ justify-content: space-between;
161+ }
162+
163+ .meta {
164+ margin-top: 10px;
165+ color: rgba(245, 239, 226, 0.78);
166+ font-size: 12px;
167+ }
168+
169+ .actions {
170+ margin-top: 12px;
171+ flex-wrap: wrap;
172+ }
173+
174+ .panel-btn {
175+ padding: 8px 10px;
176+ border-radius: 999px;
177+ background: rgba(255, 255, 255, 0.08);
178+ color: inherit;
179+ }
180+
181+ .panel-btn:hover {
182+ background: rgba(255, 255, 255, 0.14);
183+ }
184+
185+ .panel-btn[disabled] {
186+ opacity: 0.6;
187+ cursor: default;
188+ }
189+
190+ .error {
191+ margin-top: 10px;
192+ color: #ffd3bf;
193+ font-size: 12px;
194+ line-height: 1.45;
195+ }
196+ </style>
197+ <div class="wrap">
198+ <button id="baa-toggle" class="toggle" type="button">
199+ <strong>BAA</strong>
200+ <span id="baa-mode-badge" class="badge unknown">未知</span>
201+ </button>
202+ <div id="baa-panel" class="panel hidden">
203+ <div class="topline">
204+ <span id="baa-leader">主控 -</span>
205+ <button id="baa-open-panel" class="panel-btn" type="button">管理页</button>
206+ </div>
207+ <div class="meta">
208+ <span id="baa-queue">队列 -</span>
209+ <span id="baa-runs">运行 -</span>
210+ </div>
211+ <div class="actions">
212+ <button data-action="pause" class="panel-btn" type="button">暂停</button>
213+ <button data-action="resume" class="panel-btn" type="button">恢复</button>
214+ <button data-action="drain" class="panel-btn" type="button">排空</button>
215+ </div>
216+ <div id="baa-error" class="error hidden"></div>
217+ </div>
218+ </div>
219+ `;
220+
221+ claudeEntryState.host = host;
222+ claudeEntryState.shadow = shadow;
223+
224+ shadow.getElementById("baa-toggle").addEventListener("click", () => {
225+ claudeEntryState.expanded = !claudeEntryState.expanded;
226+ renderClaudeEntry();
227+ });
228+
229+ shadow.getElementById("baa-open-panel").addEventListener("click", () => {
230+ browser.runtime.sendMessage({ type: "focus_controller" }).catch(() => {});
231+ claudeEntryState.expanded = false;
232+ renderClaudeEntry();
233+ });
234+
235+ for (const actionButton of shadow.querySelectorAll("[data-action]")) {
236+ actionButton.addEventListener("click", () => {
237+ runClaudeControlAction(actionButton.dataset.action || "").catch(() => {});
238+ });
239+ }
240+
241+ document.documentElement.append(host);
242+ renderClaudeEntry();
243+}
244+
245+function renderClaudeEntry() {
246+ if (!claudeEntryState.shadow) return;
247+
248+ const snapshot = normalizeEntrySnapshot(claudeEntryState.snapshot);
249+ const badge = claudeEntryState.shadow.getElementById("baa-mode-badge");
250+ const panel = claudeEntryState.shadow.getElementById("baa-panel");
251+ const leader = claudeEntryState.shadow.getElementById("baa-leader");
252+ const queue = claudeEntryState.shadow.getElementById("baa-queue");
253+ const runs = claudeEntryState.shadow.getElementById("baa-runs");
254+ const error = claudeEntryState.shadow.getElementById("baa-error");
255+
256+ badge.textContent = formatModeLabel(snapshot.mode);
257+ badge.className = `badge ${snapshot.mode}`;
258+ leader.textContent = `主控 ${snapshot.leader || "-"}`;
259+ queue.textContent = `队列 ${snapshot.queueDepth == null ? "-" : snapshot.queueDepth}`;
260+ runs.textContent = `运行 ${snapshot.activeRuns == null ? "-" : snapshot.activeRuns}`;
261+ panel.classList.toggle("hidden", !claudeEntryState.expanded);
262+
263+ const busy = !!claudeEntryState.busyAction;
264+ for (const button of claudeEntryState.shadow.querySelectorAll(".panel-btn")) {
265+ if (button.id === "baa-open-panel") continue;
266+ button.disabled = busy;
267+ }
268+
269+ const errorText = claudeEntryState.error || snapshot.error;
270+ error.textContent = errorText || "";
271+ error.classList.toggle("hidden", !errorText);
272+}
273+
274+async function loadClaudeEntrySnapshot() {
275+ const data = await browser.storage.local.get(CONTROL_STATE_KEY);
276+ claudeEntryState.snapshot = normalizeEntrySnapshot(data[CONTROL_STATE_KEY]);
277+ renderClaudeEntry();
278+}
279+
280+async function runClaudeControlAction(action) {
281+ if (!action) return;
282+
283+ claudeEntryState.busyAction = action;
284+ claudeEntryState.error = "";
285+ renderClaudeEntry();
286+
287+ try {
288+ const response = await browser.runtime.sendMessage({
289+ type: "control_plane_command",
290+ action,
291+ source: "claude_entry"
292+ });
293+
294+ if (response?.snapshot) {
295+ claudeEntryState.snapshot = normalizeEntrySnapshot(response.snapshot);
296+ }
297+
298+ if (!response?.ok) {
299+ throw new Error(response?.error || `${action} 执行失败`);
300+ }
301+ } catch (error) {
302+ claudeEntryState.error = error.message;
303+ } finally {
304+ claudeEntryState.busyAction = null;
305+ renderClaudeEntry();
306+ }
307+}
308+
309+sendBridgeMessage("baa_page_bridge_ready", {
310+ url: location.href,
311+ source: "content-script"
312+});
313+
314+window.addEventListener("__baa_ready__", (event) => {
315+ sendBridgeMessage("baa_page_bridge_ready", {
316+ ...(event.detail || {}),
317+ url: event.detail?.url || location.href,
318+ source: event.detail?.source || "page-interceptor"
319+ });
320+});
321+
322+window.addEventListener("__baa_net__", (event) => {
323+ sendBridgeMessage("baa_page_network", event.detail);
324+});
325+
326+window.addEventListener("__baa_sse__", (event) => {
327+ sendBridgeMessage("baa_page_sse", event.detail);
328+});
329+
330+window.addEventListener("__baa_proxy_response__", (event) => {
331+ sendBridgeMessage("baa_page_proxy_response", event.detail);
332+});
333+
334+browser.runtime.onMessage.addListener((message) => {
335+ if (!message || typeof message !== "object") return undefined;
336+ if (message.type !== "baa_page_proxy_request") return undefined;
337+
338+ window.dispatchEvent(new CustomEvent("__baa_proxy_request__", {
339+ detail: JSON.stringify(message.data || {})
340+ }));
341+
342+ return undefined;
343+});
344+
345+browser.storage.onChanged.addListener((changes, areaName) => {
346+ if (areaName !== "local" || !changes[CONTROL_STATE_KEY]) return;
347+ claudeEntryState.snapshot = normalizeEntrySnapshot(changes[CONTROL_STATE_KEY].newValue);
348+ renderClaudeEntry();
349+});
350+
351+if (document.readyState === "loading") {
352+ document.addEventListener("DOMContentLoaded", () => {
353+ ensureClaudeEntry();
354+ loadClaudeEntrySnapshot().catch(() => {});
355+ }, { once: true });
356+} else {
357+ ensureClaudeEntry();
358+ loadClaudeEntrySnapshot().catch(() => {});
359+}
+211,
-0
1@@ -0,0 +1,211 @@
2+:root {
3+ --bg: #f3efe3;
4+ --panel: rgba(255, 251, 240, 0.92);
5+ --ink: #1b1b1b;
6+ --muted: #625e52;
7+ --line: rgba(27, 27, 27, 0.14);
8+ --accent: #1f6f5f;
9+ --warn: #a6512f;
10+ --off: #7f7a70;
11+}
12+
13+* {
14+ box-sizing: border-box;
15+}
16+
17+body {
18+ margin: 0;
19+ min-height: 100vh;
20+ color: var(--ink);
21+ background:
22+ radial-gradient(circle at top left, rgba(31, 111, 95, 0.18), transparent 35%),
23+ radial-gradient(circle at top right, rgba(166, 81, 47, 0.18), transparent 28%),
24+ linear-gradient(180deg, #faf6ea 0%, var(--bg) 100%);
25+ font-family: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif;
26+}
27+
28+.shell {
29+ max-width: 1120px;
30+ margin: 0 auto;
31+ padding: 24px;
32+}
33+
34+.topbar {
35+ display: flex;
36+ justify-content: space-between;
37+ gap: 16px;
38+ align-items: end;
39+ margin-bottom: 18px;
40+}
41+
42+.eyebrow {
43+ margin: 0 0 4px;
44+ font-size: 12px;
45+ text-transform: uppercase;
46+ letter-spacing: 0.18em;
47+ color: var(--muted);
48+}
49+
50+h1, h2, p {
51+ margin: 0;
52+}
53+
54+h1 {
55+ font-size: 42px;
56+ line-height: 1;
57+}
58+
59+h2 {
60+ font-size: 18px;
61+}
62+
63+.actions,
64+.settings {
65+ display: flex;
66+ gap: 10px;
67+ align-items: center;
68+ flex-wrap: wrap;
69+}
70+
71+.settings {
72+ margin-bottom: 18px;
73+ padding: 14px;
74+ border: 1px solid var(--line);
75+ border-radius: 18px;
76+ background: var(--panel);
77+ backdrop-filter: blur(12px);
78+}
79+
80+label {
81+ font-size: 13px;
82+ color: var(--muted);
83+}
84+
85+input,
86+button {
87+ font: inherit;
88+}
89+
90+input {
91+ min-width: 320px;
92+ padding: 10px 12px;
93+ border-radius: 999px;
94+ border: 1px solid var(--line);
95+ background: rgba(255, 255, 255, 0.72);
96+ color: var(--ink);
97+}
98+
99+button {
100+ padding: 10px 14px;
101+ border-radius: 999px;
102+ border: 1px solid var(--line);
103+ background: #fffdf6;
104+ color: var(--ink);
105+ cursor: pointer;
106+}
107+
108+button:hover {
109+ border-color: rgba(27, 27, 27, 0.28);
110+}
111+
112+.grid {
113+ display: grid;
114+ grid-template-columns: repeat(4, minmax(0, 1fr));
115+ gap: 14px;
116+ margin-bottom: 18px;
117+}
118+
119+.card,
120+.panel {
121+ border: 1px solid var(--line);
122+ border-radius: 20px;
123+ background: var(--panel);
124+ backdrop-filter: blur(12px);
125+}
126+
127+.card {
128+ padding: 18px;
129+}
130+
131+.label {
132+ font-size: 12px;
133+ letter-spacing: 0.14em;
134+ text-transform: uppercase;
135+ color: var(--muted);
136+}
137+
138+.value {
139+ margin-top: 12px;
140+ font-size: 28px;
141+ line-height: 1;
142+}
143+
144+.value.on {
145+ color: var(--accent);
146+}
147+
148+.value.warn {
149+ color: var(--warn);
150+}
151+
152+.value.off {
153+ color: var(--off);
154+}
155+
156+.meta {
157+ margin-top: 10px;
158+ font-size: 13px;
159+ color: var(--muted);
160+}
161+
162+.panel {
163+ margin-bottom: 18px;
164+ overflow: hidden;
165+}
166+
167+.panel-head {
168+ display: flex;
169+ justify-content: space-between;
170+ align-items: center;
171+ padding: 16px 18px;
172+ border-bottom: 1px solid var(--line);
173+}
174+
175+.code {
176+ margin: 0;
177+ padding: 18px;
178+ min-height: 100px;
179+ max-height: 300px;
180+ overflow: auto;
181+ font-family: ui-monospace, "SFMono-Regular", "Menlo", monospace;
182+ font-size: 12px;
183+ line-height: 1.55;
184+ white-space: pre-wrap;
185+ word-break: break-word;
186+}
187+
188+@media (max-width: 900px) {
189+ .grid {
190+ grid-template-columns: repeat(2, minmax(0, 1fr));
191+ }
192+}
193+
194+@media (max-width: 640px) {
195+ .shell {
196+ padding: 16px;
197+ }
198+
199+ .topbar {
200+ flex-direction: column;
201+ align-items: start;
202+ }
203+
204+ .grid {
205+ grid-template-columns: 1fr;
206+ }
207+
208+ input {
209+ min-width: 0;
210+ width: 100%;
211+ }
212+}
+128,
-0
1@@ -0,0 +1,128 @@
2+<!doctype html>
3+<html lang="zh-CN">
4+<head>
5+ <meta charset="utf-8">
6+ <meta name="viewport" content="width=device-width,initial-scale=1">
7+ <title>BAA Firefox 管理页</title>
8+ <link rel="stylesheet" href="controller.css">
9+</head>
10+<body>
11+ <main class="shell">
12+ <section class="topbar">
13+ <div>
14+ <p class="eyebrow">BAA Firefox 控制台</p>
15+ <h1>管理页面</h1>
16+ </div>
17+ <div class="actions">
18+ <button id="focus-controller-btn" type="button">聚焦本页</button>
19+ <button id="open-claude-btn" type="button">打开 Claude</button>
20+ <button id="open-chatgpt-btn" type="button">打开 ChatGPT</button>
21+ <button id="open-gemini-btn" type="button">打开 Gemini</button>
22+ <button id="reconnect-btn" type="button">重连 WS</button>
23+ </div>
24+ </section>
25+
26+ <section class="settings">
27+ <label for="ws-url">BAA WS</label>
28+ <input id="ws-url" type="text" spellcheck="false">
29+ <button id="save-ws-btn" type="button">保存</button>
30+ <label for="control-base-url">控制 API</label>
31+ <input id="control-base-url" type="text" spellcheck="false" placeholder="http://127.0.0.1:9800">
32+ <label for="control-token">令牌</label>
33+ <input id="control-token" type="password" spellcheck="false" placeholder="browser_session token">
34+ <button id="save-control-btn" type="button">保存控制面配置</button>
35+ <button id="refresh-control-btn" type="button">刷新状态</button>
36+ <button id="pause-btn" type="button">暂停</button>
37+ <button id="resume-btn" type="button">恢复</button>
38+ <button id="drain-btn" type="button">排空</button>
39+ </section>
40+
41+ <section class="grid">
42+ <article class="card">
43+ <p class="label">WS</p>
44+ <p id="ws-status" class="value off">未连接</p>
45+ <p id="client-id" class="meta">客户端: -</p>
46+ </article>
47+
48+ <article class="card">
49+ <p class="label">跟踪标签页</p>
50+ <p id="tab-status" class="value off">0 / 3</p>
51+ <p id="tab-meta" class="meta">标签页: -</p>
52+ </article>
53+
54+ <article class="card">
55+ <p class="label">凭证</p>
56+ <p id="cred-status" class="value off">0 / 3</p>
57+ <p id="cred-meta" class="meta">请求头: 0</p>
58+ </article>
59+
60+ <article class="card">
61+ <p class="label">端点</p>
62+ <p id="endpoint-count" class="value">0</p>
63+ <p class="meta">自动探测</p>
64+ </article>
65+
66+ <article class="card">
67+ <p class="label">自动化模式</p>
68+ <p id="control-mode" class="value off">未知</p>
69+ <p id="control-meta" class="meta">未同步</p>
70+ </article>
71+
72+ <article class="card">
73+ <p class="label">主控</p>
74+ <p id="leader-value" class="value off">-</p>
75+ <p id="leader-meta" class="meta">租约: -</p>
76+ </article>
77+
78+ <article class="card">
79+ <p class="label">队列</p>
80+ <p id="queue-value" class="value off">-</p>
81+ <p id="queue-meta" class="meta">排队任务</p>
82+ </article>
83+
84+ <article class="card">
85+ <p class="label">活动运行</p>
86+ <p id="runs-value" class="value off">-</p>
87+ <p id="runs-meta" class="meta">控制面</p>
88+ </article>
89+ </section>
90+
91+ <section class="panel">
92+ <div class="panel-head">
93+ <h2>控制面</h2>
94+ </div>
95+ <pre id="control-view" class="code"></pre>
96+ </section>
97+
98+ <section class="panel">
99+ <div class="panel-head">
100+ <h2>平台状态</h2>
101+ </div>
102+ <pre id="platforms-view" class="code"></pre>
103+ </section>
104+
105+ <section class="panel">
106+ <div class="panel-head">
107+ <h2>最新请求头</h2>
108+ </div>
109+ <pre id="headers-view" class="code"></pre>
110+ </section>
111+
112+ <section class="panel">
113+ <div class="panel-head">
114+ <h2>已发现端点</h2>
115+ </div>
116+ <pre id="endpoints-view" class="code"></pre>
117+ </section>
118+
119+ <section class="panel">
120+ <div class="panel-head">
121+ <h2>日志</h2>
122+ </div>
123+ <pre id="log-view" class="code"></pre>
124+ </section>
125+ </main>
126+
127+ <script src="controller.js"></script>
128+</body>
129+</html>
+1774,
-0
1@@ -0,0 +1,1774 @@
2+const LEGACY_STORAGE_KEYS = {
3+ claudeTabId: "baaFirefox.claudeTabId",
4+ endpoints: "baaFirefox.endpoints",
5+ lastHeaders: "baaFirefox.lastHeaders",
6+ lastCredentialAt: "baaFirefox.lastCredentialAt"
7+};
8+
9+const CONTROLLER_STORAGE_KEYS = {
10+ clientId: "baaFirefox.clientId",
11+ wsUrl: "baaFirefox.wsUrl",
12+ controlBaseUrl: "baaFirefox.controlBaseUrl",
13+ controlToken: "baaFirefox.controlToken",
14+ controlState: "baaFirefox.controlState",
15+ trackedTabs: "baaFirefox.trackedTabs",
16+ endpointsByPlatform: "baaFirefox.endpointsByPlatform",
17+ lastHeadersByPlatform: "baaFirefox.lastHeadersByPlatform",
18+ lastCredentialAtByPlatform: "baaFirefox.lastCredentialAtByPlatform",
19+ geminiSendTemplate: "baaFirefox.geminiSendTemplate"
20+};
21+
22+const DEFAULT_WS_URL = "ws://127.0.0.1:9800";
23+const CREDENTIAL_SEND_INTERVAL = 30_000;
24+const NETWORK_BODY_LIMIT = 5000;
25+const LOG_LIMIT = 160;
26+const PROXY_MESSAGE_RETRY = 10;
27+const PROXY_MESSAGE_RETRY_DELAY = 400;
28+const CONTROL_REFRESH_INTERVAL = 15_000;
29+const CONTROL_STATUS_BODY_LIMIT = 12_000;
30+const FORBIDDEN_PROXY_HEADER_NAMES = new Set([
31+ "accept-encoding",
32+ "connection",
33+ "content-length",
34+ "cookie",
35+ "host",
36+ "origin",
37+ "referer",
38+ "user-agent"
39+]);
40+
41+function hostnameMatches(hostname, hosts) {
42+ return hosts.some((host) => hostname === host || hostname.endsWith(`.${host}`));
43+}
44+
45+function isLikelyStaticPath(pathname = "") {
46+ const lower = pathname.toLowerCase();
47+ return lower.startsWith("/_next/")
48+ || lower.startsWith("/assets/")
49+ || lower.startsWith("/static/")
50+ || lower.startsWith("/images/")
51+ || lower.startsWith("/fonts/")
52+ || lower.startsWith("/favicon")
53+ || /\.(?:js|mjs|css|map|png|jpe?g|gif|svg|webp|ico|woff2?|ttf|otf|mp4|webm|txt)$/i.test(lower);
54+}
55+
56+const PLATFORMS = {
57+ claude: {
58+ label: "Claude",
59+ rootUrl: "https://claude.ai/",
60+ urlPatterns: ["*://claude.ai/*"],
61+ hosts: ["claude.ai"],
62+ requestUrlPatterns: ["*://claude.ai/*"],
63+ requestHosts: ["claude.ai"],
64+ matchesTabHost(hostname) {
65+ return hostnameMatches(hostname, this.hosts);
66+ },
67+ matchesRequestHost(hostname) {
68+ return hostnameMatches(hostname, this.requestHosts);
69+ },
70+ shouldTrackPath(pathname) {
71+ return pathname.includes("/api/");
72+ }
73+ },
74+ chatgpt: {
75+ label: "ChatGPT",
76+ rootUrl: "https://chatgpt.com/",
77+ urlPatterns: ["*://chatgpt.com/*", "*://*.chatgpt.com/*", "*://chat.openai.com/*", "*://*.chat.openai.com/*"],
78+ hosts: ["chatgpt.com", "chat.openai.com"],
79+ requestUrlPatterns: [
80+ "*://chatgpt.com/*",
81+ "*://*.chatgpt.com/*",
82+ "*://openai.com/*",
83+ "*://*.openai.com/*",
84+ "*://chat.openai.com/*",
85+ "*://*.chat.openai.com/*",
86+ "*://oaiusercontent.com/*",
87+ "*://*.oaiusercontent.com/*"
88+ ],
89+ requestHosts: ["chatgpt.com", "chat.openai.com", "openai.com", "oaiusercontent.com"],
90+ matchesTabHost(hostname) {
91+ return hostnameMatches(hostname, this.hosts);
92+ },
93+ matchesRequestHost(hostname) {
94+ return hostnameMatches(hostname, this.requestHosts);
95+ },
96+ shouldTrackPath(pathname) {
97+ const lower = pathname.toLowerCase();
98+ return pathname.includes("/backend-api/")
99+ || pathname.includes("/backend-anon/")
100+ || pathname.includes("/public-api/")
101+ || lower.includes("/conversation")
102+ || lower.includes("/models")
103+ || lower.includes("/files")
104+ || (!isLikelyStaticPath(pathname) && (lower.includes("/api/") || lower.includes("/backend")));
105+ }
106+ },
107+ gemini: {
108+ label: "Gemini",
109+ rootUrl: "https://gemini.google.com/",
110+ urlPatterns: ["*://gemini.google.com/*"],
111+ hosts: ["gemini.google.com"],
112+ requestUrlPatterns: ["*://gemini.google.com/*"],
113+ requestHosts: ["gemini.google.com"],
114+ matchesTabHost(hostname) {
115+ return hostnameMatches(hostname, this.hosts);
116+ },
117+ matchesRequestHost(hostname) {
118+ return hostnameMatches(hostname, this.requestHosts);
119+ },
120+ shouldTrackPath(pathname) {
121+ const lower = pathname.toLowerCase();
122+ return pathname.startsWith("/_/")
123+ || pathname.includes("/api/")
124+ || lower.includes("bardchatui")
125+ || lower.includes("streamgenerate")
126+ || lower.includes("generatecontent")
127+ || lower.includes("modelresponse")
128+ || (!isLikelyStaticPath(pathname) && lower.includes("assistant"));
129+ }
130+ }
131+};
132+
133+const PLATFORM_ORDER = Object.keys(PLATFORMS);
134+const PLATFORM_REQUEST_URL_PATTERNS = PLATFORM_ORDER.flatMap((platform) => PLATFORMS[platform].requestUrlPatterns || PLATFORMS[platform].urlPatterns);
135+const pendingProxyRequests = new Map();
136+
137+const state = {
138+ clientId: null,
139+ wsUrl: DEFAULT_WS_URL,
140+ controlBaseUrl: "",
141+ controlToken: "",
142+ controlState: null,
143+ ws: null,
144+ wsConnected: false,
145+ reconnectTimer: null,
146+ controlRefreshTimer: null,
147+ trackedTabs: createPlatformMap(() => null),
148+ endpoints: createPlatformMap(() => ({})),
149+ lastHeaders: createPlatformMap(() => ({})),
150+ lastCredentialAt: createPlatformMap(() => 0),
151+ lastCredentialHash: createPlatformMap(() => ""),
152+ geminiSendTemplate: null,
153+ logs: []
154+};
155+
156+const ui = {};
157+
158+function qs(id) {
159+ return document.getElementById(id);
160+}
161+
162+function isRecord(value) {
163+ return !!value && typeof value === "object" && !Array.isArray(value);
164+}
165+
166+function trimTrailingSlash(value) {
167+ return String(value || "").trim().replace(/\/+$/u, "");
168+}
169+
170+function deriveControlBaseUrl(wsUrl) {
171+ try {
172+ const parsed = new URL(wsUrl || DEFAULT_WS_URL);
173+ parsed.protocol = parsed.protocol === "wss:" ? "https:" : "http:";
174+ return trimTrailingSlash(parsed.toString());
175+ } catch (_) {
176+ return "http://127.0.0.1:9800";
177+ }
178+}
179+
180+function createDefaultControlState(overrides = {}) {
181+ return {
182+ mode: "unknown",
183+ leader: null,
184+ leaseHolder: null,
185+ queueDepth: null,
186+ activeRuns: null,
187+ ok: false,
188+ error: null,
189+ message: null,
190+ statusCode: null,
191+ syncedAt: 0,
192+ source: "bootstrap",
193+ raw: null,
194+ ...overrides
195+ };
196+}
197+
198+function cloneControlState(value) {
199+ if (!isRecord(value)) return createDefaultControlState();
200+ return {
201+ ...createDefaultControlState(),
202+ ...value
203+ };
204+}
205+
206+function createPlatformMap(factory) {
207+ const out = {};
208+ for (const platform of PLATFORM_ORDER) {
209+ out[platform] = factory(platform);
210+ }
211+ return out;
212+}
213+
214+function cloneHeaderMap(value) {
215+ return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
216+}
217+
218+function hasPlatformShape(value) {
219+ return !!value
220+ && typeof value === "object"
221+ && !Array.isArray(value)
222+ && PLATFORM_ORDER.some((platform) => Object.prototype.hasOwnProperty.call(value, platform));
223+}
224+
225+function loadTrackedTabs(raw, legacyClaudeTabId) {
226+ const next = createPlatformMap(() => null);
227+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
228+ for (const platform of PLATFORM_ORDER) {
229+ next[platform] = Number.isInteger(raw[platform]) ? raw[platform] : null;
230+ }
231+ }
232+ if (Number.isInteger(legacyClaudeTabId) && !Number.isInteger(next.claude)) {
233+ next.claude = legacyClaudeTabId;
234+ }
235+ return next;
236+}
237+
238+function loadObjectMap(raw, legacyValue = null) {
239+ const next = createPlatformMap(() => ({}));
240+ if (hasPlatformShape(raw)) {
241+ for (const platform of PLATFORM_ORDER) {
242+ next[platform] = cloneHeaderMap(raw[platform]);
243+ }
244+ return next;
245+ }
246+
247+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
248+ next.claude = cloneHeaderMap(raw);
249+ return next;
250+ }
251+
252+ if (legacyValue && typeof legacyValue === "object" && !Array.isArray(legacyValue)) {
253+ next.claude = cloneHeaderMap(legacyValue);
254+ }
255+ return next;
256+}
257+
258+function loadNumberMap(raw, legacyValue = 0) {
259+ const next = createPlatformMap(() => 0);
260+ if (hasPlatformShape(raw)) {
261+ for (const platform of PLATFORM_ORDER) {
262+ next[platform] = Number(raw[platform]) || 0;
263+ }
264+ return next;
265+ }
266+
267+ if (Number.isFinite(raw)) {
268+ next.claude = raw;
269+ return next;
270+ }
271+
272+ if (Number.isFinite(legacyValue)) {
273+ next.claude = legacyValue;
274+ }
275+ return next;
276+}
277+
278+function loadControlState(raw) {
279+ if (!isRecord(raw)) return createDefaultControlState();
280+ return cloneControlState(raw);
281+}
282+
283+function getNestedValue(source, path) {
284+ const segments = String(path || "").split(".");
285+ let current = source;
286+
287+ for (const segment of segments) {
288+ if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, segment)) {
289+ return undefined;
290+ }
291+ current = current[segment];
292+ }
293+
294+ return current;
295+}
296+
297+function getFirstDefinedValue(source, paths) {
298+ for (const path of paths) {
299+ const value = getNestedValue(source, path);
300+ if (value !== undefined && value !== null && value !== "") {
301+ return value;
302+ }
303+ }
304+ return undefined;
305+}
306+
307+function normalizeMode(value) {
308+ const lower = String(value || "").trim().toLowerCase();
309+ if (lower === "running" || lower === "paused" || lower === "draining") {
310+ return lower;
311+ }
312+ return "unknown";
313+}
314+
315+function formatModeLabel(mode) {
316+ switch (normalizeMode(mode)) {
317+ case "running":
318+ return "运行中";
319+ case "paused":
320+ return "已暂停";
321+ case "draining":
322+ return "排空中";
323+ default:
324+ return "未知";
325+ }
326+}
327+
328+function normalizeLeader(value) {
329+ if (typeof value === "string" && value.trim()) return value.trim();
330+ if (isRecord(value)) {
331+ const candidate = getFirstDefinedValue(value, ["host", "node_id", "nodeId", "lease_holder", "leaseHolder", "controller_id", "controllerId"]);
332+ return typeof candidate === "string" && candidate.trim() ? candidate.trim() : null;
333+ }
334+ return null;
335+}
336+
337+function normalizeCount(value) {
338+ if (Number.isFinite(value)) return Number(value);
339+ if (typeof value === "string" && value.trim()) {
340+ const parsed = Number(value);
341+ return Number.isFinite(parsed) ? parsed : null;
342+ }
343+ if (Array.isArray(value)) return value.length;
344+ if (isRecord(value)) {
345+ const candidate = getFirstDefinedValue(value, ["count", "depth", "total", "size", "value"]);
346+ return normalizeCount(candidate);
347+ }
348+ return null;
349+}
350+
351+function truncateControlRaw(value) {
352+ if (value == null) return null;
353+
354+ try {
355+ const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
356+ return text.length > CONTROL_STATUS_BODY_LIMIT
357+ ? `${text.slice(0, CONTROL_STATUS_BODY_LIMIT)}\n…`
358+ : text;
359+ } catch (_) {
360+ return String(value);
361+ }
362+}
363+
364+function formatSyncTime(timestamp) {
365+ if (!Number.isFinite(timestamp) || timestamp <= 0) return "未同步";
366+ return new Date(timestamp).toLocaleString("zh-CN", { hour12: false });
367+}
368+
369+function controlModeClass(snapshot) {
370+ if (!snapshot || snapshot.mode === "unknown") return "off";
371+ if (snapshot.error) return "warn";
372+ return snapshot.mode === "running" ? "on" : "warn";
373+}
374+
375+function normalizeControlStatePayload(payload, meta = {}) {
376+ const source = isRecord(payload) ? payload : {};
377+ const mode = normalizeMode(getFirstDefinedValue(source, [
378+ "mode",
379+ "automation.mode",
380+ "system.mode",
381+ "system_state.mode",
382+ "systemState.mode",
383+ "state.mode",
384+ "data.mode",
385+ "data.automation.mode",
386+ "data.system.mode",
387+ "data.system_state.mode",
388+ "value.mode",
389+ "value_json.mode"
390+ ]));
391+ const leader = normalizeLeader(getFirstDefinedValue(source, [
392+ "leader",
393+ "leader.host",
394+ "data.leader",
395+ "data.leader.host",
396+ "leader_host",
397+ "leaderHost",
398+ "lease_holder",
399+ "leaseHolder"
400+ ]));
401+ const leaseHolder = normalizeLeader(getFirstDefinedValue(source, [
402+ "lease_holder",
403+ "leaseHolder",
404+ "leader.lease_holder",
405+ "leader.leaseHolder",
406+ "data.lease_holder",
407+ "data.leaseHolder"
408+ ]));
409+ const queueDepth = normalizeCount(getFirstDefinedValue(source, [
410+ "queue_depth",
411+ "queueDepth",
412+ "queued_tasks",
413+ "queuedTasks",
414+ "queue.depth",
415+ "queue.count",
416+ "stats.queue_depth",
417+ "stats.queueDepth",
418+ "data.queue_depth",
419+ "data.queueDepth",
420+ "data.queued_tasks",
421+ "data.queuedTasks",
422+ "data.queue.depth",
423+ "data.stats.queue_depth",
424+ "data.stats.queueDepth"
425+ ]));
426+ const activeRuns = normalizeCount(getFirstDefinedValue(source, [
427+ "active_runs",
428+ "activeRuns",
429+ "runs.active",
430+ "runs.active_count",
431+ "stats.active_runs",
432+ "stats.activeRuns",
433+ "data.active_runs",
434+ "data.activeRuns",
435+ "data.runs.active",
436+ "data.stats.active_runs",
437+ "data.stats.activeRuns"
438+ ]));
439+ const message = getFirstDefinedValue(source, ["message", "data.message"]);
440+ const apiReportedError = getFirstDefinedValue(source, ["error", "data.error"]);
441+ const ok = meta.ok !== false && source.ok !== false && !apiReportedError;
442+
443+ return createDefaultControlState({
444+ ok,
445+ mode,
446+ leader,
447+ leaseHolder,
448+ queueDepth,
449+ activeRuns,
450+ message: typeof message === "string" ? message : null,
451+ error: ok ? null : String(apiReportedError || message || meta.error || "控制面错误"),
452+ statusCode: Number.isFinite(meta.statusCode) ? meta.statusCode : null,
453+ syncedAt: Date.now(),
454+ source: meta.source || "http",
455+ raw: truncateControlRaw(payload)
456+ });
457+}
458+
459+function genClientId() {
460+ return `firefox-${Math.random().toString(36).slice(2, 8)}`;
461+}
462+
463+function platformLabel(platform) {
464+ return PLATFORMS[platform]?.label || platform;
465+}
466+
467+function detectPlatformFromUrl(url) {
468+ try {
469+ const hostname = new URL(url, location.href).hostname;
470+ for (const platform of PLATFORM_ORDER) {
471+ if (PLATFORMS[platform].matchesTabHost(hostname)) return platform;
472+ }
473+ } catch (_) {}
474+ return null;
475+}
476+
477+function detectPlatformFromRequestUrl(url) {
478+ try {
479+ const hostname = new URL(url, location.href).hostname;
480+ for (const platform of PLATFORM_ORDER) {
481+ if (PLATFORMS[platform].matchesRequestHost(hostname)) return platform;
482+ }
483+ } catch (_) {}
484+ return null;
485+}
486+
487+function isPlatformUrl(platform, url) {
488+ return detectPlatformFromUrl(url) === platform;
489+}
490+
491+function shouldTrackRequest(platform, url) {
492+ const config = PLATFORMS[platform];
493+ if (!config) return false;
494+ try {
495+ const parsed = new URL(url, location.href);
496+ if (!config.matchesRequestHost(parsed.hostname)) return false;
497+ return config.shouldTrackPath(parsed.pathname || "/");
498+ } catch (_) {
499+ return false;
500+ }
501+}
502+
503+function findTrackedPlatformByTabId(tabId) {
504+ if (!Number.isInteger(tabId)) return null;
505+ for (const platform of PLATFORM_ORDER) {
506+ if (state.trackedTabs[platform] === tabId) return platform;
507+ }
508+ return null;
509+}
510+
511+function addLog(level, text, sendRemote = true) {
512+ const line = `[${new Date().toLocaleTimeString("zh-CN", { hour12: false })}] [${level}] ${text}`;
513+ state.logs.push(line);
514+ if (state.logs.length > LOG_LIMIT) state.logs.shift();
515+ render();
516+
517+ if (sendRemote) {
518+ wsSend({
519+ type: "client_log",
520+ clientId: state.clientId,
521+ level,
522+ text
523+ });
524+ }
525+}
526+
527+function normalizePath(url) {
528+ try {
529+ const parsed = new URL(url);
530+ let path = parsed.pathname || "/";
531+ path = path.replace(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi, "{id}");
532+ path = path.replace(/\/\d{6,}\b/g, "/{id}");
533+ path = path.replace(/\/[a-f0-9]{24,}\b/gi, "/{id}");
534+ return path;
535+ } catch (_) {
536+ return url;
537+ }
538+}
539+
540+function headerArrayToObject(headers = []) {
541+ const out = {};
542+ for (const header of headers) {
543+ if (!header || !header.name) continue;
544+ out[header.name.toLowerCase()] = header.value || "";
545+ }
546+ return out;
547+}
548+
549+function mergeKnownHeaders(platform, extra = {}) {
550+ const known = state.lastHeaders[platform] || {};
551+ if (Object.keys(known).length === 0) return extra;
552+ return { ...known, ...extra };
553+}
554+
555+function trimBody(body) {
556+ if (body == null) return null;
557+ const text = typeof body === "string" ? body : JSON.stringify(body);
558+ return text.length > NETWORK_BODY_LIMIT ? text.slice(0, NETWORK_BODY_LIMIT) : text;
559+}
560+
561+function normalizeStoredUrl(url, baseUrl = location.href) {
562+ try {
563+ const parsed = new URL(url, baseUrl);
564+ return `${parsed.pathname || "/"}${parsed.search || ""}`;
565+ } catch (_) {
566+ return url;
567+ }
568+}
569+
570+function isGeminiStreamGenerateUrl(url) {
571+ try {
572+ const parsed = new URL(url, PLATFORMS.gemini.rootUrl);
573+ return (parsed.pathname || "").toLowerCase().includes("streamgenerate");
574+ } catch (_) {
575+ return String(url || "").toLowerCase().includes("streamgenerate");
576+ }
577+}
578+
579+function hasGeminiTemplateHeaders(headers) {
580+ const source = headers && typeof headers === "object" ? headers : {};
581+ const contentType = String(source["content-type"] || "").toLowerCase();
582+ return contentType.startsWith("application/x-www-form-urlencoded")
583+ && (source["x-same-domain"] === "1" || Object.keys(source).some((name) => name.startsWith("x-goog-ext-")));
584+}
585+
586+function parseGeminiSendTemplate(url, reqBody, reqHeaders = null) {
587+ if (!isGeminiStreamGenerateUrl(url) || typeof reqBody !== "string" || !reqBody) return null;
588+
589+ try {
590+ const normalizedUrl = normalizeStoredUrl(url, PLATFORMS.gemini.rootUrl);
591+ const parsedUrl = new URL(normalizedUrl, PLATFORMS.gemini.rootUrl);
592+ const headers = cloneHeaderMap(reqHeaders);
593+ if (Object.keys(headers).length > 0 && !hasGeminiTemplateHeaders(headers)) return null;
594+ const params = new URLSearchParams(reqBody);
595+ const outerPayload = params.get("f.req");
596+ if (!outerPayload) return null;
597+
598+ const outer = JSON.parse(outerPayload);
599+ if (!Array.isArray(outer) || typeof outer[1] !== "string") return null;
600+
601+ const inner = JSON.parse(outer[1]);
602+ if (!Array.isArray(inner) || !Array.isArray(inner[0])) return null;
603+
604+ return {
605+ url: normalizedUrl,
606+ reqBody,
607+ headers,
608+ prompt: typeof inner[0][0] === "string" ? inner[0][0] : "",
609+ reqId: Number(parsedUrl.searchParams.get("_reqid")) || null,
610+ updatedAt: Date.now()
611+ };
612+ } catch (_) {
613+ return null;
614+ }
615+}
616+
617+function rememberGeminiSendTemplate(url, reqBody, reqHeaders = null) {
618+ const next = parseGeminiSendTemplate(url, reqBody, reqHeaders);
619+ if (!next) return false;
620+
621+ const previous = state.geminiSendTemplate;
622+ if (Number.isFinite(previous?.reqId) && Number.isFinite(next.reqId)) {
623+ next.reqId = Math.max(previous.reqId, next.reqId);
624+ } else if (Number.isFinite(previous?.reqId) && !Number.isFinite(next.reqId)) {
625+ next.reqId = previous.reqId;
626+ }
627+
628+ const changed = !previous
629+ || previous.url !== next.url
630+ || previous.reqBody !== next.reqBody
631+ || previous.prompt !== next.prompt
632+ || previous.reqId !== next.reqId
633+ || JSON.stringify(previous.headers || {}) !== JSON.stringify(next.headers || {});
634+
635+ state.geminiSendTemplate = next;
636+ persistState().catch(() => {});
637+
638+ if (changed) {
639+ addLog("info", `已捕获 Gemini 发送模板 reqid=${next.reqId || "-"}`);
640+ }
641+ return true;
642+}
643+
644+function extractGeminiXsrfToken(body) {
645+ const match = String(body || "").match(/"xsrf","([^"]+)"/);
646+ return match ? match[1] : null;
647+}
648+
649+function updateGeminiTemplateXsrf(xsrfToken) {
650+ if (!xsrfToken || !state.geminiSendTemplate?.reqBody) return false;
651+
652+ const params = new URLSearchParams(state.geminiSendTemplate.reqBody);
653+ params.set("at", xsrfToken);
654+ state.geminiSendTemplate = {
655+ ...state.geminiSendTemplate,
656+ reqBody: params.toString(),
657+ updatedAt: Date.now()
658+ };
659+ persistState().catch(() => {});
660+ return true;
661+}
662+
663+function extractPromptFromProxyBody(body) {
664+ if (typeof body === "string") {
665+ const text = body.trim();
666+ if (!text) return null;
667+ if (text.includes("f.req=") || text.includes("&at=") || text.startsWith("{") || text.startsWith("[")) {
668+ return null;
669+ }
670+ return text;
671+ }
672+ if (!body || typeof body !== "object") return null;
673+
674+ const prompt = body.text ?? body.prompt ?? body.message;
675+ return typeof prompt === "string" && prompt.trim() ? prompt.trim() : null;
676+}
677+
678+function sleep(ms) {
679+ return new Promise((resolve) => setTimeout(resolve, ms));
680+}
681+
682+async function persistState() {
683+ await browser.storage.local.set({
684+ [CONTROLLER_STORAGE_KEYS.clientId]: state.clientId,
685+ [CONTROLLER_STORAGE_KEYS.wsUrl]: state.wsUrl,
686+ [CONTROLLER_STORAGE_KEYS.controlBaseUrl]: state.controlBaseUrl,
687+ [CONTROLLER_STORAGE_KEYS.controlToken]: state.controlToken,
688+ [CONTROLLER_STORAGE_KEYS.controlState]: state.controlState,
689+ [CONTROLLER_STORAGE_KEYS.trackedTabs]: state.trackedTabs,
690+ [CONTROLLER_STORAGE_KEYS.endpointsByPlatform]: state.endpoints,
691+ [CONTROLLER_STORAGE_KEYS.lastHeadersByPlatform]: state.lastHeaders,
692+ [CONTROLLER_STORAGE_KEYS.lastCredentialAtByPlatform]: state.lastCredentialAt,
693+ [CONTROLLER_STORAGE_KEYS.geminiSendTemplate]: state.geminiSendTemplate
694+ });
695+}
696+
697+function getTrackedCount() {
698+ return PLATFORM_ORDER.filter((platform) => Number.isInteger(state.trackedTabs[platform])).length;
699+}
700+
701+function getCredentialCount() {
702+ return PLATFORM_ORDER.filter((platform) => Object.keys(state.lastHeaders[platform]).length > 0).length;
703+}
704+
705+function getEndpointCount(platform) {
706+ return Object.keys(state.endpoints[platform] || {}).length;
707+}
708+
709+function getTotalEndpointCount() {
710+ return PLATFORM_ORDER.reduce((sum, platform) => sum + getEndpointCount(platform), 0);
711+}
712+
713+function renderPlatformStatus() {
714+ const lines = [];
715+ for (const platform of PLATFORM_ORDER) {
716+ const tabId = Number.isInteger(state.trackedTabs[platform]) ? state.trackedTabs[platform] : "-";
717+ const headerCount = Object.keys(state.lastHeaders[platform]).length;
718+ const endpointCount = getEndpointCount(platform);
719+ lines.push(
720+ `${platformLabel(platform).padEnd(8)} 标签页=${String(tabId).padEnd(4)} 凭证=${String(headerCount).padEnd(3)} 端点=${endpointCount}`
721+ );
722+ }
723+ return lines.join("\n");
724+}
725+
726+function renderHeaderSnapshot() {
727+ const snapshot = {};
728+ for (const platform of PLATFORM_ORDER) {
729+ if (Object.keys(state.lastHeaders[platform]).length > 0) {
730+ snapshot[platform] = state.lastHeaders[platform];
731+ }
732+ }
733+ return Object.keys(snapshot).length > 0
734+ ? JSON.stringify(snapshot, null, 2)
735+ : "还没有凭证快照。";
736+}
737+
738+function renderEndpointSnapshot() {
739+ const lines = [];
740+ for (const platform of PLATFORM_ORDER) {
741+ const endpoints = Object.keys(state.endpoints[platform]).sort();
742+ if (endpoints.length === 0) continue;
743+ lines.push(`[${platformLabel(platform)}]`);
744+ lines.push(...endpoints);
745+ lines.push("");
746+ }
747+ if (lines.length === 0) return "还没有发现端点。";
748+ if (lines[lines.length - 1] === "") lines.pop();
749+ return lines.join("\n");
750+}
751+
752+function renderControlSnapshot() {
753+ const snapshot = cloneControlState(state.controlState);
754+
755+ return JSON.stringify({
756+ ok: snapshot.ok,
757+ mode: snapshot.mode,
758+ leader: snapshot.leader,
759+ leaseHolder: snapshot.leaseHolder,
760+ queueDepth: snapshot.queueDepth,
761+ activeRuns: snapshot.activeRuns,
762+ statusCode: snapshot.statusCode,
763+ syncedAt: snapshot.syncedAt ? new Date(snapshot.syncedAt).toISOString() : null,
764+ source: snapshot.source,
765+ error: snapshot.error,
766+ message: snapshot.message,
767+ raw: snapshot.raw
768+ }, null, 2);
769+}
770+
771+function render() {
772+ const trackedCount = getTrackedCount();
773+ const credentialCount = getCredentialCount();
774+ const totalEndpointCount = getTotalEndpointCount();
775+ const controlSnapshot = cloneControlState(state.controlState);
776+
777+ ui.wsStatus.textContent = state.wsConnected ? "已连接" : "未连接";
778+ ui.wsStatus.className = `value ${state.wsConnected ? "on" : "off"}`;
779+
780+ ui.tabStatus.textContent = `${trackedCount} / ${PLATFORM_ORDER.length}`;
781+ ui.tabStatus.className = `value ${trackedCount === 0 ? "off" : trackedCount === PLATFORM_ORDER.length ? "on" : "warn"}`;
782+ ui.tabMeta.textContent = trackedCount > 0
783+ ? PLATFORM_ORDER
784+ .filter((platform) => Number.isInteger(state.trackedTabs[platform]))
785+ .map((platform) => `${platformLabel(platform)}:${state.trackedTabs[platform]}`)
786+ .join(" | ")
787+ : "标签页: -";
788+
789+ ui.credStatus.textContent = `${credentialCount} / ${PLATFORM_ORDER.length}`;
790+ ui.credStatus.className = `value ${credentialCount === 0 ? "off" : credentialCount === PLATFORM_ORDER.length ? "on" : "warn"}`;
791+ ui.credMeta.textContent = `请求头: ${PLATFORM_ORDER.reduce((sum, platform) => sum + Object.keys(state.lastHeaders[platform]).length, 0)}`;
792+
793+ ui.endpointCount.textContent = String(totalEndpointCount);
794+ ui.endpointCount.className = `value ${totalEndpointCount > 0 ? "on" : "off"}`;
795+ ui.clientId.textContent = `客户端: ${state.clientId || "-"}`;
796+ ui.controlMode.textContent = formatModeLabel(controlSnapshot.mode);
797+ ui.controlMode.className = `value ${controlModeClass(controlSnapshot)}`;
798+ ui.controlMeta.textContent = `${formatSyncTime(controlSnapshot.syncedAt)}${controlSnapshot.error ? ` · ${controlSnapshot.error}` : ""}`;
799+ ui.leaderValue.textContent = controlSnapshot.leader || "-";
800+ ui.leaderValue.className = `value ${controlSnapshot.leader ? "on" : "off"}`;
801+ ui.leaderMeta.textContent = `租约: ${controlSnapshot.leaseHolder || "-"}`;
802+ ui.queueValue.textContent = controlSnapshot.queueDepth == null ? "-" : String(controlSnapshot.queueDepth);
803+ ui.queueValue.className = `value ${controlSnapshot.queueDepth > 0 ? "warn" : controlSnapshot.queueDepth === 0 ? "on" : "off"}`;
804+ ui.queueMeta.textContent = "排队任务";
805+ ui.runsValue.textContent = controlSnapshot.activeRuns == null ? "-" : String(controlSnapshot.activeRuns);
806+ ui.runsValue.className = `value ${controlSnapshot.activeRuns > 0 ? "warn" : controlSnapshot.activeRuns === 0 ? "on" : "off"}`;
807+ ui.runsMeta.textContent = controlSnapshot.message || "控制面";
808+ ui.platformsView.textContent = renderPlatformStatus();
809+ ui.controlView.textContent = renderControlSnapshot();
810+ ui.headersView.textContent = renderHeaderSnapshot();
811+ ui.endpointsView.textContent = renderEndpointSnapshot();
812+ ui.logView.textContent = state.logs.length > 0
813+ ? state.logs.join("\n")
814+ : "还没有日志。";
815+}
816+
817+async function setControlState(next) {
818+ state.controlState = cloneControlState(next);
819+ await persistState();
820+ render();
821+}
822+
823+async function requestControlPlane(path, options = {}) {
824+ const baseUrl = trimTrailingSlash(state.controlBaseUrl || deriveControlBaseUrl(state.wsUrl));
825+ if (!baseUrl) {
826+ throw new Error("缺少控制 API 地址");
827+ }
828+
829+ const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
830+ const headers = new Headers({
831+ Accept: "application/json"
832+ });
833+
834+ if (state.controlToken) {
835+ headers.set("Authorization", `Bearer ${state.controlToken}`);
836+ }
837+
838+ if (options.body != null && !headers.has("Content-Type")) {
839+ headers.set("Content-Type", "application/json");
840+ }
841+
842+ const response = await fetch(url, {
843+ method: options.method || "GET",
844+ headers,
845+ body: options.body == null ? undefined : options.body
846+ });
847+
848+ const responseText = await response.text();
849+ let payload = null;
850+
851+ if (responseText) {
852+ try {
853+ payload = JSON.parse(responseText);
854+ } catch (_) {
855+ payload = responseText;
856+ }
857+ }
858+
859+ if (!response.ok || (isRecord(payload) && payload.ok === false)) {
860+ const message = isRecord(payload)
861+ ? String(payload.message || payload.error || `${response.status}`)
862+ : String(responseText || response.status);
863+ const error = new Error(message);
864+ error.statusCode = response.status;
865+ error.payload = payload;
866+ throw error;
867+ }
868+
869+ return {
870+ statusCode: response.status,
871+ payload
872+ };
873+}
874+
875+async function refreshControlPlaneState(options = {}) {
876+ try {
877+ const response = await requestControlPlane("/v1/system/state");
878+ const snapshot = normalizeControlStatePayload(response.payload, {
879+ ok: true,
880+ statusCode: response.statusCode,
881+ source: options.source || "http"
882+ });
883+ await setControlState(snapshot);
884+
885+ if (!options.silent) {
886+ addLog("info", `控制面状态已同步:模式=${formatModeLabel(snapshot.mode)},主控=${snapshot.leader || "-"}`);
887+ }
888+
889+ return snapshot;
890+ } catch (error) {
891+ const snapshot = normalizeControlStatePayload(error.payload || error.message, {
892+ ok: false,
893+ statusCode: Number.isFinite(error.statusCode) ? error.statusCode : null,
894+ source: options.source || "http",
895+ error: error.message
896+ });
897+ await setControlState(snapshot);
898+
899+ if (!options.silent) {
900+ addLog("error", `控制面状态同步失败:${error.message}`);
901+ }
902+
903+ throw error;
904+ }
905+}
906+
907+async function runControlPlaneAction(action, options = {}) {
908+ const methodName = String(action || "").trim().toLowerCase();
909+
910+ if (!["pause", "resume", "drain"].includes(methodName)) {
911+ throw new Error(`未知控制动作:${action || "-"}`);
912+ }
913+
914+ const response = await requestControlPlane(`/v1/system/${methodName}`, {
915+ method: "POST"
916+ });
917+
918+ addLog("info", `控制动作 ${methodName} 已接受(${response.statusCode})`);
919+
920+ try {
921+ await refreshControlPlaneState({
922+ source: options.source || "http",
923+ silent: true
924+ });
925+ } catch (_) {}
926+
927+ return {
928+ action: methodName,
929+ statusCode: response.statusCode,
930+ payload: response.payload,
931+ snapshot: cloneControlState(state.controlState)
932+ };
933+}
934+
935+function restartControlPlaneRefreshTimer() {
936+ clearInterval(state.controlRefreshTimer);
937+ state.controlRefreshTimer = setInterval(() => {
938+ refreshControlPlaneState({ source: "poll", silent: true }).catch(() => {});
939+ }, CONTROL_REFRESH_INTERVAL);
940+}
941+
942+function wsSend(payload) {
943+ if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return false;
944+ state.ws.send(JSON.stringify(payload));
945+ return true;
946+}
947+
948+function sendApiResponse(id, ok, status = null, body = null, error = null) {
949+ wsSend({
950+ type: "api_response",
951+ id,
952+ ok,
953+ status,
954+ body,
955+ error
956+ });
957+}
958+
959+function sendHello() {
960+ wsSend({
961+ type: "hello",
962+ clientId: state.clientId,
963+ nodeType: "browser",
964+ nodeCategory: "proxy",
965+ nodePlatform: "firefox"
966+ });
967+}
968+
969+function getTargetPlatforms(platform) {
970+ if (platform && PLATFORMS[platform]) return [platform];
971+ return PLATFORM_ORDER;
972+}
973+
974+function getProxyHeaderPath(apiPath) {
975+ try {
976+ return new URL(apiPath, "https://example.invalid").pathname || "/";
977+ } catch (_) {
978+ return apiPath || "/";
979+ }
980+}
981+
982+function isForbiddenProxyHeader(name) {
983+ return FORBIDDEN_PROXY_HEADER_NAMES.has(name) || name.startsWith("sec-");
984+}
985+
986+function copyProxyHeaders(sourceHeaders = {}) {
987+ const out = {};
988+
989+ for (const [name, value] of Object.entries(sourceHeaders || {})) {
990+ const lower = String(name || "").toLowerCase();
991+ if (!lower || value == null || value === "" || isForbiddenProxyHeader(lower)) continue;
992+ out[lower] = value;
993+ }
994+ return out;
995+}
996+
997+function buildProxyHeaders(platform, apiPath, sourceHeaders = null) {
998+ const targetPath = getProxyHeaderPath(apiPath);
999+ const out = copyProxyHeaders(sourceHeaders || state.lastHeaders[platform] || {});
1000+
1001+ if (platform === "chatgpt") {
1002+ out["x-openai-target-path"] = targetPath;
1003+ out["x-openai-target-route"] = targetPath;
1004+ if (!out["x-conduit-token"]) out["x-conduit-token"] = "no-token";
1005+ if (!out["x-oai-turn-trace-id"] && typeof crypto?.randomUUID === "function") {
1006+ out["x-oai-turn-trace-id"] = crypto.randomUUID();
1007+ }
1008+ }
1009+
1010+ return out;
1011+}
1012+
1013+function buildGeminiAutoRequest(prompt) {
1014+ const template = state.geminiSendTemplate;
1015+ if (!template?.url || !template?.reqBody) {
1016+ throw new Error("missing Gemini send template; send one real Gemini message first");
1017+ }
1018+
1019+ try {
1020+ const url = new URL(template.url, PLATFORMS.gemini.rootUrl);
1021+ const params = new URLSearchParams(template.reqBody);
1022+ const outerPayload = params.get("f.req");
1023+ if (!outerPayload) throw new Error("template missing f.req");
1024+
1025+ const outer = JSON.parse(outerPayload);
1026+ if (!Array.isArray(outer) || typeof outer[1] !== "string") {
1027+ throw new Error("invalid Gemini outer payload");
1028+ }
1029+
1030+ const inner = JSON.parse(outer[1]);
1031+ if (!Array.isArray(inner) || !Array.isArray(inner[0])) {
1032+ throw new Error("无效的 Gemini 提示词元组");
1033+ }
1034+
1035+ inner[0][0] = prompt;
1036+ outer[1] = JSON.stringify(inner);
1037+ params.set("f.req", JSON.stringify(outer));
1038+
1039+ const currentReqId = Number(url.searchParams.get("_reqid"));
1040+ const baseReqId = Number.isFinite(template.reqId) ? template.reqId : currentReqId;
1041+ if (Number.isFinite(baseReqId)) {
1042+ const nextReqId = baseReqId + 100000;
1043+ url.searchParams.set("_reqid", String(nextReqId));
1044+ state.geminiSendTemplate = {
1045+ ...template,
1046+ reqId: nextReqId,
1047+ updatedAt: Date.now()
1048+ };
1049+ persistState().catch(() => {});
1050+ }
1051+
1052+ const path = `${url.pathname || "/"}${url.search || ""}`;
1053+ const headerSource = hasGeminiTemplateHeaders(template.headers)
1054+ ? template.headers
1055+ : state.lastHeaders.gemini;
1056+ const headers = buildProxyHeaders("gemini", path, headerSource);
1057+ if (!hasGeminiTemplateHeaders(headers)) {
1058+ throw new Error("缺少 Gemini 请求头;请先手动发送一条真实 Gemini 消息");
1059+ }
1060+
1061+ return {
1062+ path,
1063+ body: params.toString(),
1064+ headers
1065+ };
1066+ } catch (error) {
1067+ throw new Error(`构建 Gemini 请求失败:${error.message}`);
1068+ }
1069+}
1070+
1071+function sendEndpointSnapshot(platform = null) {
1072+ for (const target of getTargetPlatforms(platform)) {
1073+ const endpoints = Object.keys(state.endpoints[target]).sort();
1074+ if (endpoints.length === 0) continue;
1075+ wsSend({
1076+ type: "api_endpoints",
1077+ platform: target,
1078+ endpoints
1079+ });
1080+ }
1081+}
1082+
1083+function sendCredentialSnapshot(platform = null, force = false) {
1084+ let changed = false;
1085+
1086+ for (const target of getTargetPlatforms(platform)) {
1087+ const headers = state.lastHeaders[target];
1088+ if (Object.keys(headers).length === 0) continue;
1089+
1090+ const now = Date.now();
1091+ const serialized = JSON.stringify(headers);
1092+ if (!force && serialized === state.lastCredentialHash[target] && now - state.lastCredentialAt[target] < CREDENTIAL_SEND_INTERVAL) {
1093+ continue;
1094+ }
1095+
1096+ state.lastCredentialHash[target] = serialized;
1097+ state.lastCredentialAt[target] = now;
1098+ changed = true;
1099+
1100+ wsSend({
1101+ type: "credentials",
1102+ platform: target,
1103+ headers,
1104+ timestamp: now
1105+ });
1106+ }
1107+
1108+ if (changed) {
1109+ persistState().catch(() => {});
1110+ }
1111+}
1112+
1113+function connectWs() {
1114+ clearTimeout(state.reconnectTimer);
1115+
1116+ if (state.ws) {
1117+ state.ws.onopen = null;
1118+ state.ws.onmessage = null;
1119+ state.ws.onclose = null;
1120+ state.ws.onerror = null;
1121+ try {
1122+ state.ws.close();
1123+ } catch (_) {}
1124+ }
1125+
1126+ addLog("info", `正在连接 WS:${state.wsUrl}`, false);
1127+
1128+ try {
1129+ state.ws = new WebSocket(state.wsUrl);
1130+ } catch (error) {
1131+ addLog("error", `WS 创建失败:${error.message}`, false);
1132+ scheduleReconnect();
1133+ return;
1134+ }
1135+
1136+ state.ws.onopen = () => {
1137+ state.wsConnected = true;
1138+ render();
1139+ sendHello();
1140+ addLog("info", "WS 已连接");
1141+ sendCredentialSnapshot(null, true);
1142+ sendEndpointSnapshot();
1143+ };
1144+
1145+ state.ws.onmessage = (event) => {
1146+ let message = null;
1147+ try {
1148+ message = JSON.parse(event.data);
1149+ } catch (_) {
1150+ return;
1151+ }
1152+
1153+ if (!message || typeof message !== "object") return;
1154+
1155+ switch (message.type) {
1156+ case "open_tab": {
1157+ const targets = getTargetPlatforms(message.platform);
1158+ for (const target of targets) {
1159+ ensurePlatformTab(target, { focus: targets.length === 1 }).catch(() => {});
1160+ }
1161+ break;
1162+ }
1163+ case "api_request":
1164+ proxyApiRequest(message).catch((error) => {
1165+ sendApiResponse(message.id, false, null, null, error.message);
1166+ addLog("error", `代理 ${message.platform || "未知"} ${message.method || "GET"} ${message.path || "-"} 失败:${error.message}`);
1167+ });
1168+ break;
1169+ case "request_credentials":
1170+ sendCredentialSnapshot(message.platform || null, true);
1171+ break;
1172+ case "reload":
1173+ addLog("warn", "收到重载命令");
1174+ window.location.reload();
1175+ break;
1176+ default:
1177+ break;
1178+ }
1179+ };
1180+
1181+ state.ws.onclose = (event) => {
1182+ state.wsConnected = false;
1183+ render();
1184+ addLog("warn", `WS 已关闭 code=${event.code || 0} reason=${event.reason || "-"}`, false);
1185+ scheduleReconnect();
1186+ };
1187+
1188+ state.ws.onerror = () => {
1189+ state.wsConnected = false;
1190+ render();
1191+ addLog("error", `WS 错误:${state.wsUrl}`, false);
1192+ };
1193+}
1194+
1195+function scheduleReconnect() {
1196+ clearTimeout(state.reconnectTimer);
1197+ state.reconnectTimer = setTimeout(() => {
1198+ connectWs();
1199+ }, 3000);
1200+}
1201+
1202+async function resolveTrackedTab(platform) {
1203+ const tabId = state.trackedTabs[platform];
1204+ if (!Number.isInteger(tabId)) return null;
1205+ try {
1206+ const tab = await browser.tabs.get(tabId);
1207+ if (!tab || !tab.url || !isPlatformUrl(platform, tab.url)) return null;
1208+ return tab;
1209+ } catch (_) {
1210+ return null;
1211+ }
1212+}
1213+
1214+async function findPlatformTab(platform) {
1215+ const tabs = await browser.tabs.query({ url: PLATFORMS[platform].urlPatterns });
1216+ if (tabs.length === 0) return null;
1217+ tabs.sort((left, right) => {
1218+ if (!!left.active !== !!right.active) return left.active ? -1 : 1;
1219+ const leftAccess = Number(left.lastAccessed) || 0;
1220+ const rightAccess = Number(right.lastAccessed) || 0;
1221+ return rightAccess - leftAccess;
1222+ });
1223+ return tabs[0];
1224+}
1225+
1226+async function setTrackedTab(platform, tab) {
1227+ state.trackedTabs[platform] = tab ? tab.id : null;
1228+ await persistState();
1229+ render();
1230+}
1231+
1232+async function ensurePlatformTab(platform, options = {}) {
1233+ const { focus = false, reloadIfExisting = false } = options;
1234+ let existed = true;
1235+ let tab = await resolveTrackedTab(platform);
1236+ if (!tab) {
1237+ tab = await findPlatformTab(platform);
1238+ }
1239+
1240+ if (!tab) {
1241+ existed = false;
1242+ tab = await browser.tabs.create({
1243+ url: PLATFORMS[platform].rootUrl,
1244+ active: focus
1245+ });
1246+ addLog("info", `已打开 ${platformLabel(platform)} 标签页 ${tab.id}`);
1247+ } else if (focus) {
1248+ await browser.tabs.update(tab.id, { active: true });
1249+ if (tab.windowId != null) {
1250+ await browser.windows.update(tab.windowId, { focused: true });
1251+ }
1252+ }
1253+
1254+ await setTrackedTab(platform, tab);
1255+
1256+ if (existed && reloadIfExisting) {
1257+ try {
1258+ await browser.tabs.reload(tab.id);
1259+ addLog("info", `已重新加载现有 ${platformLabel(platform)} 标签页 ${tab.id}`);
1260+ } catch (error) {
1261+ addLog("error", `重新加载 ${platformLabel(platform)} 失败:${error.message}`);
1262+ }
1263+ }
1264+
1265+ return tab;
1266+}
1267+
1268+async function ensureAllPlatformTabs(options = {}) {
1269+ for (const platform of PLATFORM_ORDER) {
1270+ await ensurePlatformTab(platform, { focus: false, ...options });
1271+ }
1272+}
1273+
1274+function collectEndpoint(platform, method, url) {
1275+ if (!shouldTrackRequest(platform, url)) return;
1276+
1277+ const key = `${(method || "GET").toUpperCase()} ${normalizePath(url)}`;
1278+ if (state.endpoints[platform][key]) return;
1279+
1280+ state.endpoints[platform][key] = true;
1281+ persistState().catch(() => {});
1282+ render();
1283+ addLog("info", `已发现 ${platformLabel(platform)} 端点 ${key}`);
1284+ sendEndpointSnapshot(platform);
1285+}
1286+
1287+function buildNetworkEntry(platform, data, tabId) {
1288+ return {
1289+ ts: new Date().toISOString(),
1290+ platform,
1291+ tabId,
1292+ url: data.url,
1293+ method: data.method,
1294+ reqHeaders: mergeKnownHeaders(platform, data.reqHeaders || {}),
1295+ reqBody: trimBody(data.reqBody),
1296+ status: data.status,
1297+ resHeaders: data.resHeaders || null,
1298+ resBody: trimBody(data.resBody),
1299+ error: data.error || null,
1300+ duration: data.duration || null,
1301+ source: data.source || "page"
1302+ };
1303+}
1304+
1305+function ensureTrackedTabId(platform, tabId, source) {
1306+ if (!Number.isInteger(tabId) || tabId < 0) return false;
1307+
1308+ const current = state.trackedTabs[platform];
1309+ if (current === tabId) return true;
1310+
1311+ state.trackedTabs[platform] = tabId;
1312+ persistState().catch(() => {});
1313+ render();
1314+
1315+ if (!Number.isInteger(current)) {
1316+ addLog("info", `已接管 ${platformLabel(platform)} 标签页 ${tabId},来源 ${source}`);
1317+ } else {
1318+ addLog("warn", `已切换 ${platformLabel(platform)} 标签页 ${current} -> ${tabId},来源 ${source}`);
1319+ }
1320+ return true;
1321+}
1322+
1323+function getSenderContext(sender, fallbackPlatform = null) {
1324+ const tabId = sender?.tab?.id;
1325+ const senderPlatform = detectPlatformFromUrl(sender?.tab?.url || "");
1326+ const platform = senderPlatform || fallbackPlatform;
1327+ if (!platform) return null;
1328+ if (!ensureTrackedTabId(platform, tabId, "message")) return null;
1329+ return { platform, tabId };
1330+}
1331+
1332+function resolvePlatformFromRequest(details) {
1333+ const platform = findTrackedPlatformByTabId(details.tabId) || detectPlatformFromRequestUrl(details.url);
1334+ if (!platform) return null;
1335+ return ensureTrackedTabId(platform, details.tabId, "request") ? platform : null;
1336+}
1337+
1338+function handlePageNetwork(data, sender) {
1339+ const context = getSenderContext(sender, data?.platform || null);
1340+ if (!context || !data || !data.url || !data.method) return;
1341+
1342+ if (context.platform === "gemini" && typeof data.reqBody === "string" && data.reqBody) {
1343+ const templateHeaders = Object.keys(data.reqHeaders || {}).length > 0
1344+ ? mergeKnownHeaders(context.platform, data.reqHeaders || {})
1345+ : cloneHeaderMap(state.lastHeaders.gemini);
1346+ rememberGeminiSendTemplate(data.url, data.reqBody, templateHeaders);
1347+ }
1348+
1349+ collectEndpoint(context.platform, data.method, data.url);
1350+ const entry = buildNetworkEntry(context.platform, data, context.tabId);
1351+ wsSend({
1352+ type: "network_log",
1353+ clientId: state.clientId,
1354+ platform: context.platform,
1355+ entry
1356+ });
1357+
1358+ if (data.status >= 400 || data.error) {
1359+ addLog("error", `${platformLabel(context.platform)} ${data.method} ${normalizePath(data.url)} -> ${data.status || data.error}`);
1360+ }
1361+}
1362+
1363+function handlePageSse(data, sender) {
1364+ const context = getSenderContext(sender, data?.platform || null);
1365+ if (!context || !data || !data.url) return;
1366+
1367+ wsSend({
1368+ type: "sse_event",
1369+ clientId: state.clientId,
1370+ platform: context.platform,
1371+ url: data.url,
1372+ method: data.method,
1373+ reqBody: trimBody(data.reqBody),
1374+ chunk: data.chunk || null,
1375+ done: !!data.done,
1376+ error: data.error || null,
1377+ ts: data.ts || Date.now()
1378+ });
1379+}
1380+
1381+function handlePageProxyResponse(data, sender) {
1382+ const context = getSenderContext(sender, data?.platform || null);
1383+ if (!context || !data || !data.id) return;
1384+ const pending = pendingProxyRequests.get(data.id);
1385+
1386+ if (
1387+ context.platform === "gemini"
1388+ && pending?.prompt
1389+ && pending.attempts < 1
1390+ && Number(data.status) === 400
1391+ ) {
1392+ const xsrfToken = extractGeminiXsrfToken(data.body);
1393+ if (updateGeminiTemplateXsrf(xsrfToken)) {
1394+ pending.attempts += 1;
1395+ addLog("info", `Gemini xsrf 已刷新,正在重试代理 ${data.id}`);
1396+ try {
1397+ const retry = buildGeminiAutoRequest(pending.prompt);
1398+ postProxyRequestToTab(context.tabId, {
1399+ id: data.id,
1400+ platform: "gemini",
1401+ method: "POST",
1402+ path: retry.path,
1403+ body: retry.body,
1404+ headers: retry.headers
1405+ }).catch((error) => {
1406+ pendingProxyRequests.delete(data.id);
1407+ sendApiResponse(data.id, false, null, null, error.message);
1408+ addLog("error", `Gemini 重试 ${data.id} 失败:${error.message}`);
1409+ });
1410+ return;
1411+ } catch (error) {
1412+ pendingProxyRequests.delete(data.id);
1413+ sendApiResponse(data.id, false, null, null, error.message);
1414+ addLog("error", `Gemini 重试 ${data.id} 失败:${error.message}`);
1415+ return;
1416+ }
1417+ }
1418+ }
1419+
1420+ pendingProxyRequests.delete(data.id);
1421+
1422+ sendApiResponse(
1423+ data.id,
1424+ !data.error,
1425+ Number.isFinite(data.status) ? data.status : null,
1426+ typeof data.body === "string" ? data.body : (data.body == null ? null : JSON.stringify(data.body)),
1427+ data.error || null
1428+ );
1429+
1430+ if (!data.ok || (Number.isFinite(data.status) && data.status >= 400)) {
1431+ addLog(
1432+ "error",
1433+ `${platformLabel(context.platform)} 代理 ${data.method || "GET"} ${normalizePath(data.url || data.path || "-")} -> ${data.status || data.error || "失败"}`
1434+ );
1435+ }
1436+}
1437+
1438+function handlePageBridgeReady(data, sender) {
1439+ const tabId = sender?.tab?.id;
1440+ const senderUrl = sender?.tab?.url || data?.url || "";
1441+ const platform = detectPlatformFromUrl(senderUrl) || data?.platform || null;
1442+ if (!platform || !Number.isInteger(tabId)) return;
1443+
1444+ ensureTrackedTabId(platform, tabId, "bridge");
1445+ addLog("info", `${platformLabel(platform)} 钩子已就绪,标签页 ${tabId},来源 ${data?.source || "未知"}`);
1446+}
1447+
1448+function handleBeforeSendHeaders(details) {
1449+ const platform = resolvePlatformFromRequest(details);
1450+ if (!platform) return;
1451+
1452+ const headers = headerArrayToObject(details.requestHeaders);
1453+ if (Object.keys(headers).length === 0) return;
1454+
1455+ state.lastHeaders[platform] = headers;
1456+ collectEndpoint(platform, details.method || "GET", details.url);
1457+ render();
1458+ sendCredentialSnapshot(platform);
1459+ persistState().catch(() => {});
1460+}
1461+
1462+function handleCompleted(details) {
1463+ const platform = resolvePlatformFromRequest(details);
1464+ if (!platform || !shouldTrackRequest(platform, details.url)) return;
1465+ collectEndpoint(platform, details.method || "GET", details.url);
1466+}
1467+
1468+function handleErrorOccurred(details) {
1469+ const platform = resolvePlatformFromRequest(details);
1470+ if (!platform || !shouldTrackRequest(platform, details.url)) return;
1471+ addLog("error", `${platformLabel(platform)} ${details.method} ${normalizePath(details.url)} 失败:${details.error}`);
1472+}
1473+
1474+async function postProxyRequestToTab(tabId, data) {
1475+ let lastError = null;
1476+ for (let attempt = 0; attempt < PROXY_MESSAGE_RETRY; attempt += 1) {
1477+ try {
1478+ await browser.tabs.sendMessage(tabId, {
1479+ type: "baa_page_proxy_request",
1480+ data
1481+ });
1482+ return;
1483+ } catch (error) {
1484+ lastError = error;
1485+ if (attempt < PROXY_MESSAGE_RETRY - 1) {
1486+ await sleep(PROXY_MESSAGE_RETRY_DELAY);
1487+ }
1488+ }
1489+ }
1490+ throw lastError || new Error("无法连接内容脚本");
1491+}
1492+
1493+async function proxyApiRequest(message) {
1494+ const { id, platform, method = "GET", path: apiPath, body = null } = message || {};
1495+ if (!id) throw new Error("缺少代理请求 ID");
1496+ if (!platform || !PLATFORMS[platform]) throw new Error(`未知平台:${platform || "-"}`);
1497+ if (!apiPath) throw new Error("缺少代理请求路径");
1498+
1499+ const tab = await ensurePlatformTab(platform, { focus: false });
1500+ const prompt = platform === "gemini" ? extractPromptFromProxyBody(body) : null;
1501+ const geminiAutoRequest = platform === "gemini" && prompt && isGeminiStreamGenerateUrl(apiPath)
1502+ ? buildGeminiAutoRequest(prompt)
1503+ : null;
1504+ const payload = {
1505+ id,
1506+ platform,
1507+ method: geminiAutoRequest ? "POST" : String(method || "GET").toUpperCase(),
1508+ path: geminiAutoRequest ? geminiAutoRequest.path : apiPath,
1509+ body: geminiAutoRequest ? geminiAutoRequest.body : body,
1510+ headers: geminiAutoRequest ? geminiAutoRequest.headers : buildProxyHeaders(platform, apiPath)
1511+ };
1512+
1513+ pendingProxyRequests.set(id, {
1514+ platform,
1515+ prompt,
1516+ attempts: 0
1517+ });
1518+ try {
1519+ await postProxyRequestToTab(tab.id, payload);
1520+ } catch (error) {
1521+ pendingProxyRequests.delete(id);
1522+ throw error;
1523+ }
1524+}
1525+
1526+function registerWebRequestListeners() {
1527+ browser.webRequest.onBeforeSendHeaders.addListener(
1528+ handleBeforeSendHeaders,
1529+ { urls: PLATFORM_REQUEST_URL_PATTERNS },
1530+ ["requestHeaders"]
1531+ );
1532+
1533+ browser.webRequest.onCompleted.addListener(
1534+ handleCompleted,
1535+ { urls: PLATFORM_REQUEST_URL_PATTERNS },
1536+ ["responseHeaders"]
1537+ );
1538+
1539+ browser.webRequest.onErrorOccurred.addListener(
1540+ handleErrorOccurred,
1541+ { urls: PLATFORM_REQUEST_URL_PATTERNS }
1542+ );
1543+}
1544+
1545+function registerRuntimeListeners() {
1546+ browser.runtime.onMessage.addListener((message, sender) => {
1547+ if (!message || typeof message !== "object") return undefined;
1548+
1549+ switch (message.type) {
1550+ case "baa_page_bridge_ready":
1551+ handlePageBridgeReady(message.data, sender);
1552+ break;
1553+ case "baa_page_network":
1554+ handlePageNetwork(message.data, sender);
1555+ break;
1556+ case "baa_page_sse":
1557+ handlePageSse(message.data, sender);
1558+ break;
1559+ case "baa_page_proxy_response":
1560+ handlePageProxyResponse(message.data, sender);
1561+ break;
1562+ case "control_plane_command":
1563+ return runControlPlaneAction(message.action, {
1564+ source: message.source || "runtime"
1565+ }).then((result) => ({
1566+ ok: true,
1567+ ...result
1568+ })).catch((error) => ({
1569+ ok: false,
1570+ error: error.message,
1571+ snapshot: cloneControlState(state.controlState)
1572+ }));
1573+ case "get_control_plane_state":
1574+ return Promise.resolve({
1575+ ok: true,
1576+ snapshot: cloneControlState(state.controlState)
1577+ });
1578+ default:
1579+ break;
1580+ }
1581+
1582+ return undefined;
1583+ });
1584+}
1585+
1586+function registerTabListeners() {
1587+ browser.tabs.onActivated.addListener(async ({ tabId }) => {
1588+ try {
1589+ const tab = await browser.tabs.get(tabId);
1590+ const platform = detectPlatformFromUrl(tab?.url || "");
1591+ if (!platform) return;
1592+ ensureTrackedTabId(platform, tabId, "activation");
1593+ } catch (_) {}
1594+ });
1595+
1596+ browser.tabs.onRemoved.addListener((tabId) => {
1597+ const platform = findTrackedPlatformByTabId(tabId);
1598+ if (!platform) return;
1599+
1600+ state.trackedTabs[platform] = null;
1601+ persistState().catch(() => {});
1602+ render();
1603+ addLog("warn", `${platformLabel(platform)} tab closed, reopening`);
1604+ ensurePlatformTab(platform, { focus: false }).catch(() => {});
1605+ });
1606+
1607+ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
1608+ const platform = findTrackedPlatformByTabId(tabId);
1609+ if (!platform || changeInfo.status !== "complete") return;
1610+
1611+ if (tab?.url && isPlatformUrl(platform, tab.url)) {
1612+ addLog("info", `${platformLabel(platform)} tab ready ${tabId}`);
1613+ return;
1614+ }
1615+
1616+ state.trackedTabs[platform] = null;
1617+ persistState().catch(() => {});
1618+ render();
1619+ addLog("warn", `${platformLabel(platform)} tab moved away, reacquiring`);
1620+ ensurePlatformTab(platform, { focus: false }).catch(() => {});
1621+ });
1622+}
1623+
1624+function bindUi() {
1625+ ui.wsStatus = qs("ws-status");
1626+ ui.tabStatus = qs("tab-status");
1627+ ui.credStatus = qs("cred-status");
1628+ ui.endpointCount = qs("endpoint-count");
1629+ ui.clientId = qs("client-id");
1630+ ui.controlMode = qs("control-mode");
1631+ ui.controlMeta = qs("control-meta");
1632+ ui.leaderValue = qs("leader-value");
1633+ ui.leaderMeta = qs("leader-meta");
1634+ ui.queueValue = qs("queue-value");
1635+ ui.queueMeta = qs("queue-meta");
1636+ ui.runsValue = qs("runs-value");
1637+ ui.runsMeta = qs("runs-meta");
1638+ ui.tabMeta = qs("tab-meta");
1639+ ui.credMeta = qs("cred-meta");
1640+ ui.controlView = qs("control-view");
1641+ ui.platformsView = qs("platforms-view");
1642+ ui.headersView = qs("headers-view");
1643+ ui.endpointsView = qs("endpoints-view");
1644+ ui.logView = qs("log-view");
1645+ ui.wsUrl = qs("ws-url");
1646+ ui.controlBaseUrl = qs("control-base-url");
1647+ ui.controlToken = qs("control-token");
1648+
1649+ for (const platform of PLATFORM_ORDER) {
1650+ qs(`open-${platform}-btn`).addEventListener("click", () => {
1651+ ensurePlatformTab(platform, { focus: true }).catch((error) => {
1652+ addLog("error", `打开 ${platformLabel(platform)} 失败:${error.message}`);
1653+ });
1654+ });
1655+ }
1656+
1657+ qs("reconnect-btn").addEventListener("click", () => {
1658+ state.wsUrl = ui.wsUrl.value.trim() || DEFAULT_WS_URL;
1659+ persistState().catch(() => {});
1660+ connectWs();
1661+ });
1662+
1663+ qs("save-ws-btn").addEventListener("click", () => {
1664+ const previousDerived = deriveControlBaseUrl(state.wsUrl);
1665+ state.wsUrl = ui.wsUrl.value.trim() || DEFAULT_WS_URL;
1666+ if (!ui.controlBaseUrl.value.trim() || trimTrailingSlash(ui.controlBaseUrl.value) === previousDerived) {
1667+ state.controlBaseUrl = deriveControlBaseUrl(state.wsUrl);
1668+ }
1669+ persistState().catch(() => {});
1670+ addLog("info", `已保存 WS 地址:${state.wsUrl}`, false);
1671+ render();
1672+ });
1673+
1674+ qs("save-control-btn").addEventListener("click", () => {
1675+ state.controlBaseUrl = trimTrailingSlash(ui.controlBaseUrl.value) || deriveControlBaseUrl(state.wsUrl);
1676+ state.controlToken = ui.controlToken.value.trim();
1677+ persistState().catch(() => {});
1678+ addLog("info", `已保存控制 API:${state.controlBaseUrl}`, false);
1679+ render();
1680+ refreshControlPlaneState({ source: "manual", silent: false }).catch(() => {});
1681+ });
1682+
1683+ qs("refresh-control-btn").addEventListener("click", () => {
1684+ refreshControlPlaneState({ source: "manual", silent: false }).catch(() => {});
1685+ });
1686+
1687+ for (const action of ["pause", "resume", "drain"]) {
1688+ qs(`${action}-btn`).addEventListener("click", () => {
1689+ runControlPlaneAction(action, { source: "manual" }).catch((error) => {
1690+ addLog("error", `控制动作 ${action} 失败:${error.message}`);
1691+ });
1692+ });
1693+ }
1694+
1695+ qs("focus-controller-btn").addEventListener("click", async () => {
1696+ const tab = await browser.tabs.getCurrent();
1697+ await browser.tabs.update(tab.id, { active: true });
1698+ if (tab.windowId != null) {
1699+ await browser.windows.update(tab.windowId, { focused: true });
1700+ }
1701+ });
1702+}
1703+
1704+async function init() {
1705+ bindUi();
1706+
1707+ const saved = await browser.storage.local.get([
1708+ ...Object.values(CONTROLLER_STORAGE_KEYS),
1709+ ...Object.values(LEGACY_STORAGE_KEYS)
1710+ ]);
1711+
1712+ state.clientId = saved[CONTROLLER_STORAGE_KEYS.clientId] || genClientId();
1713+ state.wsUrl = saved[CONTROLLER_STORAGE_KEYS.wsUrl] || DEFAULT_WS_URL;
1714+ if (state.wsUrl === "ws://localhost:9800") {
1715+ state.wsUrl = DEFAULT_WS_URL;
1716+ }
1717+ state.controlBaseUrl = trimTrailingSlash(saved[CONTROLLER_STORAGE_KEYS.controlBaseUrl]) || deriveControlBaseUrl(state.wsUrl);
1718+ state.controlToken = typeof saved[CONTROLLER_STORAGE_KEYS.controlToken] === "string"
1719+ ? saved[CONTROLLER_STORAGE_KEYS.controlToken]
1720+ : "";
1721+ state.controlState = loadControlState(saved[CONTROLLER_STORAGE_KEYS.controlState]);
1722+
1723+ state.trackedTabs = loadTrackedTabs(
1724+ saved[CONTROLLER_STORAGE_KEYS.trackedTabs],
1725+ saved[LEGACY_STORAGE_KEYS.claudeTabId]
1726+ );
1727+ state.endpoints = loadObjectMap(
1728+ saved[CONTROLLER_STORAGE_KEYS.endpointsByPlatform],
1729+ saved[LEGACY_STORAGE_KEYS.endpoints]
1730+ );
1731+ state.lastHeaders = loadObjectMap(
1732+ saved[CONTROLLER_STORAGE_KEYS.lastHeadersByPlatform],
1733+ saved[LEGACY_STORAGE_KEYS.lastHeaders]
1734+ );
1735+ state.lastCredentialAt = loadNumberMap(
1736+ saved[CONTROLLER_STORAGE_KEYS.lastCredentialAtByPlatform],
1737+ saved[LEGACY_STORAGE_KEYS.lastCredentialAt]
1738+ );
1739+ state.geminiSendTemplate = saved[CONTROLLER_STORAGE_KEYS.geminiSendTemplate] || null;
1740+ state.lastCredentialHash = createPlatformMap((platform) => JSON.stringify(state.lastHeaders[platform]));
1741+
1742+ ui.wsUrl.value = state.wsUrl;
1743+ ui.controlBaseUrl.value = state.controlBaseUrl;
1744+ ui.controlToken.value = state.controlToken;
1745+
1746+ registerRuntimeListeners();
1747+ registerTabListeners();
1748+ registerWebRequestListeners();
1749+
1750+ const current = await browser.tabs.getCurrent();
1751+ await browser.runtime.sendMessage({ type: "controller_ready", tabId: current.id });
1752+ await persistState();
1753+ render();
1754+ addLog("info", `controller ready ${state.clientId}`, false);
1755+
1756+ await ensureAllPlatformTabs({ reloadIfExisting: true });
1757+ connectWs();
1758+ restartControlPlaneRefreshTimer();
1759+ refreshControlPlaneState({ source: "startup", silent: true }).catch(() => {});
1760+}
1761+
1762+window.addEventListener("beforeunload", () => {
1763+ clearTimeout(state.reconnectTimer);
1764+ clearInterval(state.controlRefreshTimer);
1765+ if (state.ws) {
1766+ try {
1767+ state.ws.close();
1768+ } catch (_) {}
1769+ }
1770+});
1771+
1772+init().catch((error) => {
1773+ console.error(error);
1774+ addLog("error", error.message, false);
1775+});
1@@ -0,0 +1,58 @@
2+# Firefox Conductor Control
3+
4+`baa-firefox` 现在包含一条最小可用的 conductor control-plane 接线。
5+
6+## 范围
7+
8+- `controller.html` / `controller.js`
9+ - 配置 Control API base URL 和 bearer token
10+ - 读取 `GET /v1/system/state`
11+ - 调用 `POST /v1/system/pause`
12+ - 调用 `POST /v1/system/resume`
13+ - 调用 `POST /v1/system/drain`
14+- `background.js`
15+ - 根据最新 control snapshot 更新扩展 badge
16+- `content-script.js`
17+ - 在 Claude 页面右下角提供最小浮层入口
18+
19+## 配置
20+
21+控制页新增了两项:
22+
23+- `Control API`
24+ - 默认从当前 `wsUrl` 推导,例如 `ws://127.0.0.1:9800` -> `http://127.0.0.1:9800`
25+- `Bearer`
26+ - 直接写入 `Authorization: Bearer <token>`
27+
28+状态快照会持久化到 `browser.storage.local` 的 `baaFirefox.controlState`,供:
29+
30+- controller 面板渲染
31+- toolbar badge 渲染
32+- Claude 页面浮层渲染
33+
34+## 状态字段
35+
36+插件会尽量从 `/v1/system/state` 响应中归一化这些字段:
37+
38+- `mode`
39+- `leader`
40+- `lease_holder`
41+- `queue_depth` / `queued_tasks`
42+- `active_runs`
43+
44+如果服务端实际字段名略有不同,`controller.js` 已做多路径兼容解析。
45+
46+## 可见入口
47+
48+- 扩展工具栏 badge
49+ - `RUN` / `PAU` / `DRN` / `ERR`
50+- `controller.html`
51+ - 可见 control-plane 卡片、原始快照和动作按钮
52+- Claude 页面
53+ - 右下角 `BAA` 浮层,可直接 `Pause` / `Resume` / `Drain` 或打开 controller
54+
55+## 当前限制
56+
57+- 本次没有改 `manifest.json`
58+- 因此 Control API 需要落在当前扩展 CSP 允许的地址范围内
59+- 最稳妥的做法是把 conductor control API 暴露在与当前 `wsUrl` 同源的本地桥接地址上
+76,
-0
1@@ -0,0 +1,76 @@
2+{
3+ "manifest_version": 3,
4+ "name": "BAA Firefox Controller",
5+ "version": "0.1.0",
6+ "description": "Firefox MVP for BAA: persistent controller page plus real AI tabs.",
7+ "permissions": [
8+ "tabs",
9+ "storage",
10+ "webRequest",
11+ "cookies",
12+ "scripting"
13+ ],
14+ "host_permissions": [
15+ "https://claude.ai/*",
16+ "https://chatgpt.com/*",
17+ "https://*.chatgpt.com/*",
18+ "https://openai.com/*",
19+ "https://*.openai.com/*",
20+ "https://chat.openai.com/*",
21+ "https://*.chat.openai.com/*",
22+ "https://oaiusercontent.com/*",
23+ "https://*.oaiusercontent.com/*",
24+ "https://gemini.google.com/*",
25+ "http://localhost/*",
26+ "http://127.0.0.1/*",
27+ "ws://localhost/*",
28+ "ws://127.0.0.1/*"
29+ ],
30+ "content_security_policy": {
31+ "extension_pages": "default-src 'self'; connect-src ws://localhost:9800 ws://127.0.0.1:9800 http://localhost:9800 http://127.0.0.1:9800"
32+ },
33+ "background": {
34+ "scripts": [
35+ "background.js"
36+ ]
37+ },
38+ "content_scripts": [
39+ {
40+ "matches": [
41+ "https://claude.ai/*",
42+ "https://chatgpt.com/*",
43+ "https://*.chatgpt.com/*",
44+ "https://chat.openai.com/*",
45+ "https://*.chat.openai.com/*",
46+ "https://gemini.google.com/*"
47+ ],
48+ "js": [
49+ "content-script.js"
50+ ],
51+ "run_at": "document_start"
52+ },
53+ {
54+ "matches": [
55+ "https://claude.ai/*",
56+ "https://chatgpt.com/*",
57+ "https://*.chatgpt.com/*",
58+ "https://chat.openai.com/*",
59+ "https://*.chat.openai.com/*",
60+ "https://gemini.google.com/*"
61+ ],
62+ "js": [
63+ "page-interceptor.js"
64+ ],
65+ "run_at": "document_start",
66+ "world": "MAIN"
67+ }
68+ ],
69+ "action": {
70+ "default_title": "BAA Firefox Controller"
71+ },
72+ "browser_specific_settings": {
73+ "gecko": {
74+ "id": "baa-firefox@makefile.so"
75+ }
76+ }
77+}
+3818,
-0
1@@ -0,0 +1,3818 @@
2+{
3+ "name": "baa-firefox",
4+ "lockfileVersion": 3,
5+ "requires": true,
6+ "packages": {
7+ "": {
8+ "dependencies": {
9+ "web-ext": "^10.0.0"
10+ }
11+ },
12+ "node_modules/@babel/code-frame": {
13+ "version": "7.29.0",
14+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
15+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
16+ "license": "MIT",
17+ "dependencies": {
18+ "@babel/helper-validator-identifier": "^7.28.5",
19+ "js-tokens": "^4.0.0",
20+ "picocolors": "^1.1.1"
21+ },
22+ "engines": {
23+ "node": ">=6.9.0"
24+ }
25+ },
26+ "node_modules/@babel/helper-validator-identifier": {
27+ "version": "7.28.5",
28+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
29+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
30+ "license": "MIT",
31+ "engines": {
32+ "node": ">=6.9.0"
33+ }
34+ },
35+ "node_modules/@babel/runtime": {
36+ "version": "7.28.6",
37+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
38+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
39+ "license": "MIT",
40+ "engines": {
41+ "node": ">=6.9.0"
42+ }
43+ },
44+ "node_modules/@devicefarmer/adbkit": {
45+ "version": "3.3.8",
46+ "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-3.3.8.tgz",
47+ "integrity": "sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==",
48+ "license": "Apache-2.0",
49+ "dependencies": {
50+ "@devicefarmer/adbkit-logcat": "^2.1.2",
51+ "@devicefarmer/adbkit-monkey": "~1.2.1",
52+ "bluebird": "~3.7",
53+ "commander": "^9.1.0",
54+ "debug": "~4.3.1",
55+ "node-forge": "^1.3.1",
56+ "split": "~1.0.1"
57+ },
58+ "bin": {
59+ "adbkit": "bin/adbkit"
60+ },
61+ "engines": {
62+ "node": ">= 0.10.4"
63+ }
64+ },
65+ "node_modules/@devicefarmer/adbkit-logcat": {
66+ "version": "2.1.3",
67+ "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit-logcat/-/adbkit-logcat-2.1.3.tgz",
68+ "integrity": "sha512-yeaGFjNBc/6+svbDeul1tNHtNChw6h8pSHAt5D+JsedUrMTN7tla7B15WLDyekxsuS2XlZHRxpuC6m92wiwCNw==",
69+ "license": "Apache-2.0",
70+ "engines": {
71+ "node": ">= 4"
72+ }
73+ },
74+ "node_modules/@devicefarmer/adbkit-monkey": {
75+ "version": "1.2.1",
76+ "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit-monkey/-/adbkit-monkey-1.2.1.tgz",
77+ "integrity": "sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==",
78+ "license": "Apache-2.0",
79+ "engines": {
80+ "node": ">= 0.10.4"
81+ }
82+ },
83+ "node_modules/@eslint-community/eslint-utils": {
84+ "version": "4.9.1",
85+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
86+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
87+ "license": "MIT",
88+ "dependencies": {
89+ "eslint-visitor-keys": "^3.4.3"
90+ },
91+ "engines": {
92+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
93+ },
94+ "funding": {
95+ "url": "https://opencollective.com/eslint"
96+ },
97+ "peerDependencies": {
98+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
99+ }
100+ },
101+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
102+ "version": "3.4.3",
103+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
104+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
105+ "license": "Apache-2.0",
106+ "engines": {
107+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
108+ },
109+ "funding": {
110+ "url": "https://opencollective.com/eslint"
111+ }
112+ },
113+ "node_modules/@eslint-community/regexpp": {
114+ "version": "4.12.2",
115+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
116+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
117+ "license": "MIT",
118+ "engines": {
119+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
120+ }
121+ },
122+ "node_modules/@eslint/config-array": {
123+ "version": "0.21.2",
124+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
125+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
126+ "license": "Apache-2.0",
127+ "dependencies": {
128+ "@eslint/object-schema": "^2.1.7",
129+ "debug": "^4.3.1",
130+ "minimatch": "^3.1.5"
131+ },
132+ "engines": {
133+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
134+ }
135+ },
136+ "node_modules/@eslint/config-helpers": {
137+ "version": "0.4.2",
138+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
139+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
140+ "license": "Apache-2.0",
141+ "dependencies": {
142+ "@eslint/core": "^0.17.0"
143+ },
144+ "engines": {
145+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
146+ }
147+ },
148+ "node_modules/@eslint/core": {
149+ "version": "0.17.0",
150+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
151+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
152+ "license": "Apache-2.0",
153+ "dependencies": {
154+ "@types/json-schema": "^7.0.15"
155+ },
156+ "engines": {
157+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
158+ }
159+ },
160+ "node_modules/@eslint/eslintrc": {
161+ "version": "3.3.5",
162+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
163+ "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
164+ "license": "MIT",
165+ "dependencies": {
166+ "ajv": "^6.14.0",
167+ "debug": "^4.3.2",
168+ "espree": "^10.0.1",
169+ "globals": "^14.0.0",
170+ "ignore": "^5.2.0",
171+ "import-fresh": "^3.2.1",
172+ "js-yaml": "^4.1.1",
173+ "minimatch": "^3.1.5",
174+ "strip-json-comments": "^3.1.1"
175+ },
176+ "engines": {
177+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
178+ },
179+ "funding": {
180+ "url": "https://opencollective.com/eslint"
181+ }
182+ },
183+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
184+ "version": "6.14.0",
185+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
186+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
187+ "license": "MIT",
188+ "dependencies": {
189+ "fast-deep-equal": "^3.1.1",
190+ "fast-json-stable-stringify": "^2.0.0",
191+ "json-schema-traverse": "^0.4.1",
192+ "uri-js": "^4.2.2"
193+ },
194+ "funding": {
195+ "type": "github",
196+ "url": "https://github.com/sponsors/epoberezkin"
197+ }
198+ },
199+ "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
200+ "version": "4.2.1",
201+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
202+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
203+ "license": "Apache-2.0",
204+ "engines": {
205+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
206+ },
207+ "funding": {
208+ "url": "https://opencollective.com/eslint"
209+ }
210+ },
211+ "node_modules/@eslint/eslintrc/node_modules/espree": {
212+ "version": "10.4.0",
213+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
214+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
215+ "license": "BSD-2-Clause",
216+ "dependencies": {
217+ "acorn": "^8.15.0",
218+ "acorn-jsx": "^5.3.2",
219+ "eslint-visitor-keys": "^4.2.1"
220+ },
221+ "engines": {
222+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
223+ },
224+ "funding": {
225+ "url": "https://opencollective.com/eslint"
226+ }
227+ },
228+ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
229+ "version": "0.4.1",
230+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
231+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
232+ "license": "MIT"
233+ },
234+ "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
235+ "version": "3.1.1",
236+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
237+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
238+ "license": "MIT",
239+ "engines": {
240+ "node": ">=8"
241+ },
242+ "funding": {
243+ "url": "https://github.com/sponsors/sindresorhus"
244+ }
245+ },
246+ "node_modules/@eslint/js": {
247+ "version": "9.39.2",
248+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
249+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
250+ "license": "MIT",
251+ "engines": {
252+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
253+ },
254+ "funding": {
255+ "url": "https://eslint.org/donate"
256+ }
257+ },
258+ "node_modules/@eslint/object-schema": {
259+ "version": "2.1.7",
260+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
261+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
262+ "license": "Apache-2.0",
263+ "engines": {
264+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
265+ }
266+ },
267+ "node_modules/@eslint/plugin-kit": {
268+ "version": "0.4.1",
269+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
270+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
271+ "license": "Apache-2.0",
272+ "dependencies": {
273+ "@eslint/core": "^0.17.0",
274+ "levn": "^0.4.1"
275+ },
276+ "engines": {
277+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
278+ }
279+ },
280+ "node_modules/@fluent/syntax": {
281+ "version": "0.19.0",
282+ "resolved": "https://registry.npmjs.org/@fluent/syntax/-/syntax-0.19.0.tgz",
283+ "integrity": "sha512-5D2qVpZrgpjtqU4eNOcWGp1gnUCgjfM+vKGE2y03kKN6z5EBhtx0qdRFbg8QuNNj8wXNoX93KJoYb+NqoxswmQ==",
284+ "license": "Apache-2.0",
285+ "engines": {
286+ "node": ">=14.0.0",
287+ "npm": ">=7.0.0"
288+ }
289+ },
290+ "node_modules/@fregante/relaxed-json": {
291+ "version": "2.0.0",
292+ "resolved": "https://registry.npmjs.org/@fregante/relaxed-json/-/relaxed-json-2.0.0.tgz",
293+ "integrity": "sha512-PyUXQWB42s4jBli435TDiYuVsadwRHnMc27YaLouINktvTWsL3FcKrRMGawTayFk46X+n5bE23RjUTWQwrukWw==",
294+ "license": "BSD-3-Clause",
295+ "engines": {
296+ "node": ">= 0.10.0"
297+ }
298+ },
299+ "node_modules/@humanfs/core": {
300+ "version": "0.19.1",
301+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
302+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
303+ "license": "Apache-2.0",
304+ "engines": {
305+ "node": ">=18.18.0"
306+ }
307+ },
308+ "node_modules/@humanfs/node": {
309+ "version": "0.16.7",
310+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
311+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
312+ "license": "Apache-2.0",
313+ "dependencies": {
314+ "@humanfs/core": "^0.19.1",
315+ "@humanwhocodes/retry": "^0.4.0"
316+ },
317+ "engines": {
318+ "node": ">=18.18.0"
319+ }
320+ },
321+ "node_modules/@humanwhocodes/module-importer": {
322+ "version": "1.0.1",
323+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
324+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
325+ "license": "Apache-2.0",
326+ "engines": {
327+ "node": ">=12.22"
328+ },
329+ "funding": {
330+ "type": "github",
331+ "url": "https://github.com/sponsors/nzakas"
332+ }
333+ },
334+ "node_modules/@humanwhocodes/retry": {
335+ "version": "0.4.3",
336+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
337+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
338+ "license": "Apache-2.0",
339+ "engines": {
340+ "node": ">=18.18"
341+ },
342+ "funding": {
343+ "type": "github",
344+ "url": "https://github.com/sponsors/nzakas"
345+ }
346+ },
347+ "node_modules/@mdn/browser-compat-data": {
348+ "version": "7.3.6",
349+ "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-7.3.6.tgz",
350+ "integrity": "sha512-eLTmNSxv2DaDO1Hq7C9lwQbThlF5+vMMUQfIl6xRPSC2q6EcFXhim4Mc9uxHNxcPvDFCdB3qviMFDdAzQVhYcw==",
351+ "license": "CC0-1.0"
352+ },
353+ "node_modules/@pinojs/redact": {
354+ "version": "0.4.0",
355+ "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
356+ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
357+ "license": "MIT"
358+ },
359+ "node_modules/@pnpm/config.env-replace": {
360+ "version": "1.1.0",
361+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
362+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
363+ "license": "MIT",
364+ "engines": {
365+ "node": ">=12.22.0"
366+ }
367+ },
368+ "node_modules/@pnpm/network.ca-file": {
369+ "version": "1.0.2",
370+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
371+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
372+ "license": "MIT",
373+ "dependencies": {
374+ "graceful-fs": "4.2.10"
375+ },
376+ "engines": {
377+ "node": ">=12.22.0"
378+ }
379+ },
380+ "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
381+ "version": "4.2.10",
382+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
383+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
384+ "license": "ISC"
385+ },
386+ "node_modules/@pnpm/npm-conf": {
387+ "version": "3.0.2",
388+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz",
389+ "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==",
390+ "license": "MIT",
391+ "dependencies": {
392+ "@pnpm/config.env-replace": "^1.1.0",
393+ "@pnpm/network.ca-file": "^1.0.1",
394+ "config-chain": "^1.1.11"
395+ },
396+ "engines": {
397+ "node": ">=12"
398+ }
399+ },
400+ "node_modules/@types/estree": {
401+ "version": "1.0.8",
402+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
403+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
404+ "license": "MIT"
405+ },
406+ "node_modules/@types/json-schema": {
407+ "version": "7.0.15",
408+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
409+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
410+ "license": "MIT"
411+ },
412+ "node_modules/@types/minimatch": {
413+ "version": "3.0.5",
414+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
415+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
416+ "license": "MIT"
417+ },
418+ "node_modules/@types/node": {
419+ "version": "25.5.0",
420+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
421+ "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
422+ "license": "MIT",
423+ "dependencies": {
424+ "undici-types": "~7.18.0"
425+ }
426+ },
427+ "node_modules/acorn": {
428+ "version": "8.16.0",
429+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
430+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
431+ "license": "MIT",
432+ "peer": true,
433+ "bin": {
434+ "acorn": "bin/acorn"
435+ },
436+ "engines": {
437+ "node": ">=0.4.0"
438+ }
439+ },
440+ "node_modules/acorn-jsx": {
441+ "version": "5.3.2",
442+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
443+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
444+ "license": "MIT",
445+ "peerDependencies": {
446+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
447+ }
448+ },
449+ "node_modules/addons-linter": {
450+ "version": "10.1.0",
451+ "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-10.1.0.tgz",
452+ "integrity": "sha512-Qo8QE/tGxaGMTQGiLPGfxDyrYJCKtsXFkyto3UGuVPb2V+Jc725U3Jjpwpo7cXoImCebueUVXC8KC8D7dpacTQ==",
453+ "license": "MPL-2.0",
454+ "dependencies": {
455+ "@fluent/syntax": "0.19.0",
456+ "@fregante/relaxed-json": "2.0.0",
457+ "@mdn/browser-compat-data": "7.3.6",
458+ "addons-moz-compare": "1.3.0",
459+ "addons-scanner-utils": "13.1.0",
460+ "ajv": "8.18.0",
461+ "chalk": "4.1.2",
462+ "cheerio": "1.2.0",
463+ "columnify": "1.6.0",
464+ "common-tags": "1.8.2",
465+ "deepmerge": "4.3.1",
466+ "eslint": "9.39.2",
467+ "eslint-plugin-no-unsanitized": "4.1.5",
468+ "eslint-visitor-keys": "5.0.1",
469+ "espree": "11.2.0",
470+ "esprima": "4.0.1",
471+ "fast-json-patch": "3.1.1",
472+ "image-size": "2.0.2",
473+ "json-merge-patch": "1.0.2",
474+ "pino": "10.3.1",
475+ "semver": "7.7.4",
476+ "source-map-support": "0.5.21",
477+ "upath": "2.0.1",
478+ "yargs": "17.7.2",
479+ "yauzl": "3.2.1"
480+ },
481+ "bin": {
482+ "addons-linter": "bin/addons-linter"
483+ },
484+ "engines": {
485+ "node": ">=20.0.0"
486+ }
487+ },
488+ "node_modules/addons-moz-compare": {
489+ "version": "1.3.0",
490+ "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz",
491+ "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==",
492+ "license": "MPL-2.0"
493+ },
494+ "node_modules/addons-scanner-utils": {
495+ "version": "13.1.0",
496+ "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-13.1.0.tgz",
497+ "integrity": "sha512-4apAnr0xrEVbIJhNPQA6XUnVAiUR0/lt8EvflrPgrmBQYwTwo7z+yv92t7F1iO2Y07lNhDCZEoL8q1H4sLJyGw==",
498+ "license": "MPL-2.0",
499+ "dependencies": {
500+ "common-tags": "1.8.2",
501+ "first-chunk-stream": "3.0.0",
502+ "jsonwebtoken": "^9.0.3",
503+ "strip-bom-stream": "4.0.0",
504+ "upath": "2.0.1",
505+ "yauzl": "3.2.1"
506+ },
507+ "peerDependencies": {
508+ "body-parser": "2.2.2",
509+ "express": "5.2.1",
510+ "safe-compare": "1.1.4"
511+ },
512+ "peerDependenciesMeta": {
513+ "body-parser": {
514+ "optional": true
515+ },
516+ "express": {
517+ "optional": true
518+ },
519+ "safe-compare": {
520+ "optional": true
521+ }
522+ }
523+ },
524+ "node_modules/adm-zip": {
525+ "version": "0.5.16",
526+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
527+ "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
528+ "license": "MIT",
529+ "engines": {
530+ "node": ">=12.0"
531+ }
532+ },
533+ "node_modules/agent-base": {
534+ "version": "7.1.4",
535+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
536+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
537+ "license": "MIT",
538+ "engines": {
539+ "node": ">= 14"
540+ }
541+ },
542+ "node_modules/ajv": {
543+ "version": "8.18.0",
544+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
545+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
546+ "license": "MIT",
547+ "dependencies": {
548+ "fast-deep-equal": "^3.1.3",
549+ "fast-uri": "^3.0.1",
550+ "json-schema-traverse": "^1.0.0",
551+ "require-from-string": "^2.0.2"
552+ },
553+ "funding": {
554+ "type": "github",
555+ "url": "https://github.com/sponsors/epoberezkin"
556+ }
557+ },
558+ "node_modules/ansi-align": {
559+ "version": "3.0.1",
560+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
561+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
562+ "license": "ISC",
563+ "dependencies": {
564+ "string-width": "^4.1.0"
565+ }
566+ },
567+ "node_modules/ansi-align/node_modules/emoji-regex": {
568+ "version": "8.0.0",
569+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
570+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
571+ "license": "MIT"
572+ },
573+ "node_modules/ansi-align/node_modules/string-width": {
574+ "version": "4.2.3",
575+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
576+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
577+ "license": "MIT",
578+ "dependencies": {
579+ "emoji-regex": "^8.0.0",
580+ "is-fullwidth-code-point": "^3.0.0",
581+ "strip-ansi": "^6.0.1"
582+ },
583+ "engines": {
584+ "node": ">=8"
585+ }
586+ },
587+ "node_modules/ansi-regex": {
588+ "version": "5.0.1",
589+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
590+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
591+ "license": "MIT",
592+ "engines": {
593+ "node": ">=8"
594+ }
595+ },
596+ "node_modules/ansi-styles": {
597+ "version": "4.3.0",
598+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
599+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
600+ "license": "MIT",
601+ "dependencies": {
602+ "color-convert": "^2.0.1"
603+ },
604+ "engines": {
605+ "node": ">=8"
606+ },
607+ "funding": {
608+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
609+ }
610+ },
611+ "node_modules/argparse": {
612+ "version": "2.0.1",
613+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
614+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
615+ "license": "Python-2.0"
616+ },
617+ "node_modules/array-differ": {
618+ "version": "4.0.0",
619+ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz",
620+ "integrity": "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==",
621+ "license": "MIT",
622+ "engines": {
623+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
624+ },
625+ "funding": {
626+ "url": "https://github.com/sponsors/sindresorhus"
627+ }
628+ },
629+ "node_modules/array-union": {
630+ "version": "3.0.1",
631+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz",
632+ "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==",
633+ "license": "MIT",
634+ "engines": {
635+ "node": ">=12"
636+ },
637+ "funding": {
638+ "url": "https://github.com/sponsors/sindresorhus"
639+ }
640+ },
641+ "node_modules/async": {
642+ "version": "3.2.6",
643+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
644+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
645+ "license": "MIT"
646+ },
647+ "node_modules/atomic-sleep": {
648+ "version": "1.0.0",
649+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
650+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
651+ "license": "MIT",
652+ "engines": {
653+ "node": ">=8.0.0"
654+ }
655+ },
656+ "node_modules/atomically": {
657+ "version": "2.1.1",
658+ "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz",
659+ "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==",
660+ "license": "MIT",
661+ "dependencies": {
662+ "stubborn-fs": "^2.0.0",
663+ "when-exit": "^2.1.4"
664+ }
665+ },
666+ "node_modules/balanced-match": {
667+ "version": "1.0.2",
668+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
669+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
670+ "license": "MIT"
671+ },
672+ "node_modules/bluebird": {
673+ "version": "3.7.2",
674+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
675+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
676+ "license": "MIT"
677+ },
678+ "node_modules/boolbase": {
679+ "version": "1.0.0",
680+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
681+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
682+ "license": "ISC"
683+ },
684+ "node_modules/boxen": {
685+ "version": "8.0.1",
686+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
687+ "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
688+ "license": "MIT",
689+ "dependencies": {
690+ "ansi-align": "^3.0.1",
691+ "camelcase": "^8.0.0",
692+ "chalk": "^5.3.0",
693+ "cli-boxes": "^3.0.0",
694+ "string-width": "^7.2.0",
695+ "type-fest": "^4.21.0",
696+ "widest-line": "^5.0.0",
697+ "wrap-ansi": "^9.0.0"
698+ },
699+ "engines": {
700+ "node": ">=18"
701+ },
702+ "funding": {
703+ "url": "https://github.com/sponsors/sindresorhus"
704+ }
705+ },
706+ "node_modules/boxen/node_modules/chalk": {
707+ "version": "5.6.2",
708+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
709+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
710+ "license": "MIT",
711+ "engines": {
712+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
713+ },
714+ "funding": {
715+ "url": "https://github.com/chalk/chalk?sponsor=1"
716+ }
717+ },
718+ "node_modules/brace-expansion": {
719+ "version": "1.1.12",
720+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
721+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
722+ "license": "MIT",
723+ "dependencies": {
724+ "balanced-match": "^1.0.0",
725+ "concat-map": "0.0.1"
726+ }
727+ },
728+ "node_modules/buffer-crc32": {
729+ "version": "0.2.13",
730+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
731+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
732+ "license": "MIT",
733+ "engines": {
734+ "node": "*"
735+ }
736+ },
737+ "node_modules/buffer-equal-constant-time": {
738+ "version": "1.0.1",
739+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
740+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
741+ "license": "BSD-3-Clause"
742+ },
743+ "node_modules/buffer-from": {
744+ "version": "1.1.2",
745+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
746+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
747+ "license": "MIT"
748+ },
749+ "node_modules/bundle-name": {
750+ "version": "4.1.0",
751+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
752+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
753+ "license": "MIT",
754+ "dependencies": {
755+ "run-applescript": "^7.0.0"
756+ },
757+ "engines": {
758+ "node": ">=18"
759+ },
760+ "funding": {
761+ "url": "https://github.com/sponsors/sindresorhus"
762+ }
763+ },
764+ "node_modules/callsites": {
765+ "version": "3.1.0",
766+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
767+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
768+ "license": "MIT",
769+ "engines": {
770+ "node": ">=6"
771+ }
772+ },
773+ "node_modules/camelcase": {
774+ "version": "8.0.0",
775+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
776+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
777+ "license": "MIT",
778+ "engines": {
779+ "node": ">=16"
780+ },
781+ "funding": {
782+ "url": "https://github.com/sponsors/sindresorhus"
783+ }
784+ },
785+ "node_modules/chalk": {
786+ "version": "4.1.2",
787+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
788+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
789+ "license": "MIT",
790+ "dependencies": {
791+ "ansi-styles": "^4.1.0",
792+ "supports-color": "^7.1.0"
793+ },
794+ "engines": {
795+ "node": ">=10"
796+ },
797+ "funding": {
798+ "url": "https://github.com/chalk/chalk?sponsor=1"
799+ }
800+ },
801+ "node_modules/cheerio": {
802+ "version": "1.2.0",
803+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz",
804+ "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==",
805+ "license": "MIT",
806+ "dependencies": {
807+ "cheerio-select": "^2.1.0",
808+ "dom-serializer": "^2.0.0",
809+ "domhandler": "^5.0.3",
810+ "domutils": "^3.2.2",
811+ "encoding-sniffer": "^0.2.1",
812+ "htmlparser2": "^10.1.0",
813+ "parse5": "^7.3.0",
814+ "parse5-htmlparser2-tree-adapter": "^7.1.0",
815+ "parse5-parser-stream": "^7.1.2",
816+ "undici": "^7.19.0",
817+ "whatwg-mimetype": "^4.0.0"
818+ },
819+ "engines": {
820+ "node": ">=20.18.1"
821+ },
822+ "funding": {
823+ "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
824+ }
825+ },
826+ "node_modules/cheerio-select": {
827+ "version": "2.1.0",
828+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
829+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
830+ "license": "BSD-2-Clause",
831+ "dependencies": {
832+ "boolbase": "^1.0.0",
833+ "css-select": "^5.1.0",
834+ "css-what": "^6.1.0",
835+ "domelementtype": "^2.3.0",
836+ "domhandler": "^5.0.3",
837+ "domutils": "^3.0.1"
838+ },
839+ "funding": {
840+ "url": "https://github.com/sponsors/fb55"
841+ }
842+ },
843+ "node_modules/chrome-launcher": {
844+ "version": "1.2.0",
845+ "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.0.tgz",
846+ "integrity": "sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==",
847+ "license": "Apache-2.0",
848+ "dependencies": {
849+ "@types/node": "*",
850+ "escape-string-regexp": "^4.0.0",
851+ "is-wsl": "^2.2.0",
852+ "lighthouse-logger": "^2.0.1"
853+ },
854+ "bin": {
855+ "print-chrome-path": "bin/print-chrome-path.cjs"
856+ },
857+ "engines": {
858+ "node": ">=12.13.0"
859+ }
860+ },
861+ "node_modules/cli-boxes": {
862+ "version": "3.0.0",
863+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
864+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
865+ "license": "MIT",
866+ "engines": {
867+ "node": ">=10"
868+ },
869+ "funding": {
870+ "url": "https://github.com/sponsors/sindresorhus"
871+ }
872+ },
873+ "node_modules/cliui": {
874+ "version": "8.0.1",
875+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
876+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
877+ "license": "ISC",
878+ "dependencies": {
879+ "string-width": "^4.2.0",
880+ "strip-ansi": "^6.0.1",
881+ "wrap-ansi": "^7.0.0"
882+ },
883+ "engines": {
884+ "node": ">=12"
885+ }
886+ },
887+ "node_modules/cliui/node_modules/emoji-regex": {
888+ "version": "8.0.0",
889+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
890+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
891+ "license": "MIT"
892+ },
893+ "node_modules/cliui/node_modules/string-width": {
894+ "version": "4.2.3",
895+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
896+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
897+ "license": "MIT",
898+ "dependencies": {
899+ "emoji-regex": "^8.0.0",
900+ "is-fullwidth-code-point": "^3.0.0",
901+ "strip-ansi": "^6.0.1"
902+ },
903+ "engines": {
904+ "node": ">=8"
905+ }
906+ },
907+ "node_modules/cliui/node_modules/wrap-ansi": {
908+ "version": "7.0.0",
909+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
910+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
911+ "license": "MIT",
912+ "dependencies": {
913+ "ansi-styles": "^4.0.0",
914+ "string-width": "^4.1.0",
915+ "strip-ansi": "^6.0.0"
916+ },
917+ "engines": {
918+ "node": ">=10"
919+ },
920+ "funding": {
921+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
922+ }
923+ },
924+ "node_modules/clone": {
925+ "version": "1.0.4",
926+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
927+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
928+ "license": "MIT",
929+ "engines": {
930+ "node": ">=0.8"
931+ }
932+ },
933+ "node_modules/color-convert": {
934+ "version": "2.0.1",
935+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
936+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
937+ "license": "MIT",
938+ "dependencies": {
939+ "color-name": "~1.1.4"
940+ },
941+ "engines": {
942+ "node": ">=7.0.0"
943+ }
944+ },
945+ "node_modules/color-name": {
946+ "version": "1.1.4",
947+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
948+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
949+ "license": "MIT"
950+ },
951+ "node_modules/columnify": {
952+ "version": "1.6.0",
953+ "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz",
954+ "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==",
955+ "license": "MIT",
956+ "dependencies": {
957+ "strip-ansi": "^6.0.1",
958+ "wcwidth": "^1.0.0"
959+ },
960+ "engines": {
961+ "node": ">=8.0.0"
962+ }
963+ },
964+ "node_modules/commander": {
965+ "version": "9.5.0",
966+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
967+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
968+ "license": "MIT",
969+ "engines": {
970+ "node": "^12.20.0 || >=14"
971+ }
972+ },
973+ "node_modules/common-tags": {
974+ "version": "1.8.2",
975+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
976+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
977+ "license": "MIT",
978+ "engines": {
979+ "node": ">=4.0.0"
980+ }
981+ },
982+ "node_modules/concat-map": {
983+ "version": "0.0.1",
984+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
985+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
986+ "license": "MIT"
987+ },
988+ "node_modules/concat-stream": {
989+ "version": "1.6.2",
990+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
991+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
992+ "engines": [
993+ "node >= 0.8"
994+ ],
995+ "license": "MIT",
996+ "dependencies": {
997+ "buffer-from": "^1.0.0",
998+ "inherits": "^2.0.3",
999+ "readable-stream": "^2.2.2",
1000+ "typedarray": "^0.0.6"
1001+ }
1002+ },
1003+ "node_modules/config-chain": {
1004+ "version": "1.1.13",
1005+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
1006+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
1007+ "license": "MIT",
1008+ "dependencies": {
1009+ "ini": "^1.3.4",
1010+ "proto-list": "~1.2.1"
1011+ }
1012+ },
1013+ "node_modules/config-chain/node_modules/ini": {
1014+ "version": "1.3.8",
1015+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
1016+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
1017+ "license": "ISC"
1018+ },
1019+ "node_modules/configstore": {
1020+ "version": "7.1.0",
1021+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz",
1022+ "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==",
1023+ "license": "BSD-2-Clause",
1024+ "dependencies": {
1025+ "atomically": "^2.0.3",
1026+ "dot-prop": "^9.0.0",
1027+ "graceful-fs": "^4.2.11",
1028+ "xdg-basedir": "^5.1.0"
1029+ },
1030+ "engines": {
1031+ "node": ">=18"
1032+ },
1033+ "funding": {
1034+ "url": "https://github.com/sponsors/sindresorhus"
1035+ }
1036+ },
1037+ "node_modules/core-util-is": {
1038+ "version": "1.0.3",
1039+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
1040+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
1041+ "license": "MIT"
1042+ },
1043+ "node_modules/cross-spawn": {
1044+ "version": "7.0.6",
1045+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
1046+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1047+ "license": "MIT",
1048+ "dependencies": {
1049+ "path-key": "^3.1.0",
1050+ "shebang-command": "^2.0.0",
1051+ "which": "^2.0.1"
1052+ },
1053+ "engines": {
1054+ "node": ">= 8"
1055+ }
1056+ },
1057+ "node_modules/css-select": {
1058+ "version": "5.2.2",
1059+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
1060+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
1061+ "license": "BSD-2-Clause",
1062+ "dependencies": {
1063+ "boolbase": "^1.0.0",
1064+ "css-what": "^6.1.0",
1065+ "domhandler": "^5.0.2",
1066+ "domutils": "^3.0.1",
1067+ "nth-check": "^2.0.1"
1068+ },
1069+ "funding": {
1070+ "url": "https://github.com/sponsors/fb55"
1071+ }
1072+ },
1073+ "node_modules/css-what": {
1074+ "version": "6.2.2",
1075+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
1076+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
1077+ "license": "BSD-2-Clause",
1078+ "engines": {
1079+ "node": ">= 6"
1080+ },
1081+ "funding": {
1082+ "url": "https://github.com/sponsors/fb55"
1083+ }
1084+ },
1085+ "node_modules/debounce": {
1086+ "version": "1.2.1",
1087+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
1088+ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
1089+ "license": "MIT"
1090+ },
1091+ "node_modules/debug": {
1092+ "version": "4.3.7",
1093+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
1094+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
1095+ "license": "MIT",
1096+ "dependencies": {
1097+ "ms": "^2.1.3"
1098+ },
1099+ "engines": {
1100+ "node": ">=6.0"
1101+ },
1102+ "peerDependenciesMeta": {
1103+ "supports-color": {
1104+ "optional": true
1105+ }
1106+ }
1107+ },
1108+ "node_modules/decamelize": {
1109+ "version": "6.0.1",
1110+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz",
1111+ "integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==",
1112+ "license": "MIT",
1113+ "engines": {
1114+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
1115+ },
1116+ "funding": {
1117+ "url": "https://github.com/sponsors/sindresorhus"
1118+ }
1119+ },
1120+ "node_modules/deep-extend": {
1121+ "version": "0.6.0",
1122+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
1123+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
1124+ "license": "MIT",
1125+ "engines": {
1126+ "node": ">=4.0.0"
1127+ }
1128+ },
1129+ "node_modules/deep-is": {
1130+ "version": "0.1.4",
1131+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
1132+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
1133+ "license": "MIT"
1134+ },
1135+ "node_modules/deepmerge": {
1136+ "version": "4.3.1",
1137+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
1138+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
1139+ "license": "MIT",
1140+ "engines": {
1141+ "node": ">=0.10.0"
1142+ }
1143+ },
1144+ "node_modules/default-browser": {
1145+ "version": "5.5.0",
1146+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
1147+ "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
1148+ "license": "MIT",
1149+ "dependencies": {
1150+ "bundle-name": "^4.1.0",
1151+ "default-browser-id": "^5.0.0"
1152+ },
1153+ "engines": {
1154+ "node": ">=18"
1155+ },
1156+ "funding": {
1157+ "url": "https://github.com/sponsors/sindresorhus"
1158+ }
1159+ },
1160+ "node_modules/default-browser-id": {
1161+ "version": "5.0.1",
1162+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
1163+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
1164+ "license": "MIT",
1165+ "engines": {
1166+ "node": ">=18"
1167+ },
1168+ "funding": {
1169+ "url": "https://github.com/sponsors/sindresorhus"
1170+ }
1171+ },
1172+ "node_modules/defaults": {
1173+ "version": "1.0.4",
1174+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
1175+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
1176+ "license": "MIT",
1177+ "dependencies": {
1178+ "clone": "^1.0.2"
1179+ },
1180+ "funding": {
1181+ "url": "https://github.com/sponsors/sindresorhus"
1182+ }
1183+ },
1184+ "node_modules/define-lazy-prop": {
1185+ "version": "3.0.0",
1186+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
1187+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
1188+ "license": "MIT",
1189+ "engines": {
1190+ "node": ">=12"
1191+ },
1192+ "funding": {
1193+ "url": "https://github.com/sponsors/sindresorhus"
1194+ }
1195+ },
1196+ "node_modules/dom-serializer": {
1197+ "version": "2.0.0",
1198+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
1199+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
1200+ "license": "MIT",
1201+ "dependencies": {
1202+ "domelementtype": "^2.3.0",
1203+ "domhandler": "^5.0.2",
1204+ "entities": "^4.2.0"
1205+ },
1206+ "funding": {
1207+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
1208+ }
1209+ },
1210+ "node_modules/domelementtype": {
1211+ "version": "2.3.0",
1212+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
1213+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
1214+ "funding": [
1215+ {
1216+ "type": "github",
1217+ "url": "https://github.com/sponsors/fb55"
1218+ }
1219+ ],
1220+ "license": "BSD-2-Clause"
1221+ },
1222+ "node_modules/domhandler": {
1223+ "version": "5.0.3",
1224+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
1225+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
1226+ "license": "BSD-2-Clause",
1227+ "dependencies": {
1228+ "domelementtype": "^2.3.0"
1229+ },
1230+ "engines": {
1231+ "node": ">= 4"
1232+ },
1233+ "funding": {
1234+ "url": "https://github.com/fb55/domhandler?sponsor=1"
1235+ }
1236+ },
1237+ "node_modules/domutils": {
1238+ "version": "3.2.2",
1239+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
1240+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
1241+ "license": "BSD-2-Clause",
1242+ "dependencies": {
1243+ "dom-serializer": "^2.0.0",
1244+ "domelementtype": "^2.3.0",
1245+ "domhandler": "^5.0.3"
1246+ },
1247+ "funding": {
1248+ "url": "https://github.com/fb55/domutils?sponsor=1"
1249+ }
1250+ },
1251+ "node_modules/dot-prop": {
1252+ "version": "9.0.0",
1253+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
1254+ "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
1255+ "license": "MIT",
1256+ "dependencies": {
1257+ "type-fest": "^4.18.2"
1258+ },
1259+ "engines": {
1260+ "node": ">=18"
1261+ },
1262+ "funding": {
1263+ "url": "https://github.com/sponsors/sindresorhus"
1264+ }
1265+ },
1266+ "node_modules/ecdsa-sig-formatter": {
1267+ "version": "1.0.11",
1268+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
1269+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
1270+ "license": "Apache-2.0",
1271+ "dependencies": {
1272+ "safe-buffer": "^5.0.1"
1273+ }
1274+ },
1275+ "node_modules/emoji-regex": {
1276+ "version": "10.6.0",
1277+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
1278+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
1279+ "license": "MIT"
1280+ },
1281+ "node_modules/encoding-sniffer": {
1282+ "version": "0.2.1",
1283+ "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
1284+ "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
1285+ "license": "MIT",
1286+ "dependencies": {
1287+ "iconv-lite": "^0.6.3",
1288+ "whatwg-encoding": "^3.1.1"
1289+ },
1290+ "funding": {
1291+ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
1292+ }
1293+ },
1294+ "node_modules/entities": {
1295+ "version": "4.5.0",
1296+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
1297+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
1298+ "license": "BSD-2-Clause",
1299+ "engines": {
1300+ "node": ">=0.12"
1301+ },
1302+ "funding": {
1303+ "url": "https://github.com/fb55/entities?sponsor=1"
1304+ }
1305+ },
1306+ "node_modules/es6-error": {
1307+ "version": "4.1.1",
1308+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
1309+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
1310+ "license": "MIT"
1311+ },
1312+ "node_modules/escalade": {
1313+ "version": "3.2.0",
1314+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1315+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1316+ "license": "MIT",
1317+ "engines": {
1318+ "node": ">=6"
1319+ }
1320+ },
1321+ "node_modules/escape-goat": {
1322+ "version": "4.0.0",
1323+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
1324+ "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
1325+ "license": "MIT",
1326+ "engines": {
1327+ "node": ">=12"
1328+ },
1329+ "funding": {
1330+ "url": "https://github.com/sponsors/sindresorhus"
1331+ }
1332+ },
1333+ "node_modules/escape-string-regexp": {
1334+ "version": "4.0.0",
1335+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
1336+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
1337+ "license": "MIT",
1338+ "engines": {
1339+ "node": ">=10"
1340+ },
1341+ "funding": {
1342+ "url": "https://github.com/sponsors/sindresorhus"
1343+ }
1344+ },
1345+ "node_modules/eslint": {
1346+ "version": "9.39.2",
1347+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
1348+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
1349+ "license": "MIT",
1350+ "dependencies": {
1351+ "@eslint-community/eslint-utils": "^4.8.0",
1352+ "@eslint-community/regexpp": "^4.12.1",
1353+ "@eslint/config-array": "^0.21.1",
1354+ "@eslint/config-helpers": "^0.4.2",
1355+ "@eslint/core": "^0.17.0",
1356+ "@eslint/eslintrc": "^3.3.1",
1357+ "@eslint/js": "9.39.2",
1358+ "@eslint/plugin-kit": "^0.4.1",
1359+ "@humanfs/node": "^0.16.6",
1360+ "@humanwhocodes/module-importer": "^1.0.1",
1361+ "@humanwhocodes/retry": "^0.4.2",
1362+ "@types/estree": "^1.0.6",
1363+ "ajv": "^6.12.4",
1364+ "chalk": "^4.0.0",
1365+ "cross-spawn": "^7.0.6",
1366+ "debug": "^4.3.2",
1367+ "escape-string-regexp": "^4.0.0",
1368+ "eslint-scope": "^8.4.0",
1369+ "eslint-visitor-keys": "^4.2.1",
1370+ "espree": "^10.4.0",
1371+ "esquery": "^1.5.0",
1372+ "esutils": "^2.0.2",
1373+ "fast-deep-equal": "^3.1.3",
1374+ "file-entry-cache": "^8.0.0",
1375+ "find-up": "^5.0.0",
1376+ "glob-parent": "^6.0.2",
1377+ "ignore": "^5.2.0",
1378+ "imurmurhash": "^0.1.4",
1379+ "is-glob": "^4.0.0",
1380+ "json-stable-stringify-without-jsonify": "^1.0.1",
1381+ "lodash.merge": "^4.6.2",
1382+ "minimatch": "^3.1.2",
1383+ "natural-compare": "^1.4.0",
1384+ "optionator": "^0.9.3"
1385+ },
1386+ "bin": {
1387+ "eslint": "bin/eslint.js"
1388+ },
1389+ "engines": {
1390+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1391+ },
1392+ "funding": {
1393+ "url": "https://eslint.org/donate"
1394+ },
1395+ "peerDependencies": {
1396+ "jiti": "*"
1397+ },
1398+ "peerDependenciesMeta": {
1399+ "jiti": {
1400+ "optional": true
1401+ }
1402+ }
1403+ },
1404+ "node_modules/eslint-plugin-no-unsanitized": {
1405+ "version": "4.1.5",
1406+ "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.5.tgz",
1407+ "integrity": "sha512-MSB4hXPVFQrI8weqzs6gzl7reP2k/qSjtCoL2vUMSDejIIq9YL1ZKvq5/ORBXab/PvfBBrWO2jWviYpL+4Ghfg==",
1408+ "license": "MPL-2.0",
1409+ "peerDependencies": {
1410+ "eslint": "^9 || ^10"
1411+ }
1412+ },
1413+ "node_modules/eslint-scope": {
1414+ "version": "8.4.0",
1415+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
1416+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
1417+ "license": "BSD-2-Clause",
1418+ "dependencies": {
1419+ "esrecurse": "^4.3.0",
1420+ "estraverse": "^5.2.0"
1421+ },
1422+ "engines": {
1423+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1424+ },
1425+ "funding": {
1426+ "url": "https://opencollective.com/eslint"
1427+ }
1428+ },
1429+ "node_modules/eslint-visitor-keys": {
1430+ "version": "5.0.1",
1431+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
1432+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
1433+ "license": "Apache-2.0",
1434+ "engines": {
1435+ "node": "^20.19.0 || ^22.13.0 || >=24"
1436+ },
1437+ "funding": {
1438+ "url": "https://opencollective.com/eslint"
1439+ }
1440+ },
1441+ "node_modules/eslint/node_modules/ajv": {
1442+ "version": "6.14.0",
1443+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
1444+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
1445+ "license": "MIT",
1446+ "dependencies": {
1447+ "fast-deep-equal": "^3.1.1",
1448+ "fast-json-stable-stringify": "^2.0.0",
1449+ "json-schema-traverse": "^0.4.1",
1450+ "uri-js": "^4.2.2"
1451+ },
1452+ "funding": {
1453+ "type": "github",
1454+ "url": "https://github.com/sponsors/epoberezkin"
1455+ }
1456+ },
1457+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
1458+ "version": "4.2.1",
1459+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
1460+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
1461+ "license": "Apache-2.0",
1462+ "engines": {
1463+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1464+ },
1465+ "funding": {
1466+ "url": "https://opencollective.com/eslint"
1467+ }
1468+ },
1469+ "node_modules/eslint/node_modules/espree": {
1470+ "version": "10.4.0",
1471+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
1472+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
1473+ "license": "BSD-2-Clause",
1474+ "dependencies": {
1475+ "acorn": "^8.15.0",
1476+ "acorn-jsx": "^5.3.2",
1477+ "eslint-visitor-keys": "^4.2.1"
1478+ },
1479+ "engines": {
1480+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1481+ },
1482+ "funding": {
1483+ "url": "https://opencollective.com/eslint"
1484+ }
1485+ },
1486+ "node_modules/eslint/node_modules/json-schema-traverse": {
1487+ "version": "0.4.1",
1488+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
1489+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
1490+ "license": "MIT"
1491+ },
1492+ "node_modules/espree": {
1493+ "version": "11.2.0",
1494+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
1495+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
1496+ "license": "BSD-2-Clause",
1497+ "dependencies": {
1498+ "acorn": "^8.16.0",
1499+ "acorn-jsx": "^5.3.2",
1500+ "eslint-visitor-keys": "^5.0.1"
1501+ },
1502+ "engines": {
1503+ "node": "^20.19.0 || ^22.13.0 || >=24"
1504+ },
1505+ "funding": {
1506+ "url": "https://opencollective.com/eslint"
1507+ }
1508+ },
1509+ "node_modules/esprima": {
1510+ "version": "4.0.1",
1511+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
1512+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
1513+ "license": "BSD-2-Clause",
1514+ "bin": {
1515+ "esparse": "bin/esparse.js",
1516+ "esvalidate": "bin/esvalidate.js"
1517+ },
1518+ "engines": {
1519+ "node": ">=4"
1520+ }
1521+ },
1522+ "node_modules/esquery": {
1523+ "version": "1.7.0",
1524+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
1525+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
1526+ "license": "BSD-3-Clause",
1527+ "dependencies": {
1528+ "estraverse": "^5.1.0"
1529+ },
1530+ "engines": {
1531+ "node": ">=0.10"
1532+ }
1533+ },
1534+ "node_modules/esrecurse": {
1535+ "version": "4.3.0",
1536+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
1537+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
1538+ "license": "BSD-2-Clause",
1539+ "dependencies": {
1540+ "estraverse": "^5.2.0"
1541+ },
1542+ "engines": {
1543+ "node": ">=4.0"
1544+ }
1545+ },
1546+ "node_modules/estraverse": {
1547+ "version": "5.3.0",
1548+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
1549+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
1550+ "license": "BSD-2-Clause",
1551+ "engines": {
1552+ "node": ">=4.0"
1553+ }
1554+ },
1555+ "node_modules/esutils": {
1556+ "version": "2.0.3",
1557+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
1558+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
1559+ "license": "BSD-2-Clause",
1560+ "engines": {
1561+ "node": ">=0.10.0"
1562+ }
1563+ },
1564+ "node_modules/fast-deep-equal": {
1565+ "version": "3.1.3",
1566+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
1567+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
1568+ "license": "MIT"
1569+ },
1570+ "node_modules/fast-json-patch": {
1571+ "version": "3.1.1",
1572+ "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
1573+ "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==",
1574+ "license": "MIT"
1575+ },
1576+ "node_modules/fast-json-stable-stringify": {
1577+ "version": "2.1.0",
1578+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
1579+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
1580+ "license": "MIT"
1581+ },
1582+ "node_modules/fast-levenshtein": {
1583+ "version": "2.0.6",
1584+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
1585+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
1586+ "license": "MIT"
1587+ },
1588+ "node_modules/fast-uri": {
1589+ "version": "3.1.0",
1590+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
1591+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
1592+ "funding": [
1593+ {
1594+ "type": "github",
1595+ "url": "https://github.com/sponsors/fastify"
1596+ },
1597+ {
1598+ "type": "opencollective",
1599+ "url": "https://opencollective.com/fastify"
1600+ }
1601+ ],
1602+ "license": "BSD-3-Clause"
1603+ },
1604+ "node_modules/file-entry-cache": {
1605+ "version": "8.0.0",
1606+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
1607+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
1608+ "license": "MIT",
1609+ "dependencies": {
1610+ "flat-cache": "^4.0.0"
1611+ },
1612+ "engines": {
1613+ "node": ">=16.0.0"
1614+ }
1615+ },
1616+ "node_modules/find-up": {
1617+ "version": "5.0.0",
1618+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
1619+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
1620+ "license": "MIT",
1621+ "dependencies": {
1622+ "locate-path": "^6.0.0",
1623+ "path-exists": "^4.0.0"
1624+ },
1625+ "engines": {
1626+ "node": ">=10"
1627+ },
1628+ "funding": {
1629+ "url": "https://github.com/sponsors/sindresorhus"
1630+ }
1631+ },
1632+ "node_modules/firefox-profile": {
1633+ "version": "4.7.0",
1634+ "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-4.7.0.tgz",
1635+ "integrity": "sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==",
1636+ "license": "MIT",
1637+ "dependencies": {
1638+ "adm-zip": "~0.5.x",
1639+ "fs-extra": "^11.2.0",
1640+ "ini": "^4.1.3",
1641+ "minimist": "^1.2.8",
1642+ "xml2js": "^0.6.2"
1643+ },
1644+ "bin": {
1645+ "firefox-profile": "lib/cli.js"
1646+ },
1647+ "engines": {
1648+ "node": ">=18"
1649+ }
1650+ },
1651+ "node_modules/first-chunk-stream": {
1652+ "version": "3.0.0",
1653+ "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz",
1654+ "integrity": "sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==",
1655+ "license": "MIT",
1656+ "engines": {
1657+ "node": ">=8"
1658+ }
1659+ },
1660+ "node_modules/flat-cache": {
1661+ "version": "4.0.1",
1662+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
1663+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
1664+ "license": "MIT",
1665+ "dependencies": {
1666+ "flatted": "^3.2.9",
1667+ "keyv": "^4.5.4"
1668+ },
1669+ "engines": {
1670+ "node": ">=16"
1671+ }
1672+ },
1673+ "node_modules/flatted": {
1674+ "version": "3.4.2",
1675+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
1676+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
1677+ "license": "ISC"
1678+ },
1679+ "node_modules/fs-extra": {
1680+ "version": "11.3.4",
1681+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz",
1682+ "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==",
1683+ "license": "MIT",
1684+ "dependencies": {
1685+ "graceful-fs": "^4.2.0",
1686+ "jsonfile": "^6.0.1",
1687+ "universalify": "^2.0.0"
1688+ },
1689+ "engines": {
1690+ "node": ">=14.14"
1691+ }
1692+ },
1693+ "node_modules/fx-runner": {
1694+ "version": "1.4.0",
1695+ "resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.4.0.tgz",
1696+ "integrity": "sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==",
1697+ "license": "MPL-2.0",
1698+ "dependencies": {
1699+ "commander": "2.9.0",
1700+ "shell-quote": "1.7.3",
1701+ "spawn-sync": "1.0.15",
1702+ "when": "3.7.7",
1703+ "which": "1.2.4",
1704+ "winreg": "0.0.12"
1705+ },
1706+ "bin": {
1707+ "fx-runner": "bin/fx-runner"
1708+ }
1709+ },
1710+ "node_modules/fx-runner/node_modules/commander": {
1711+ "version": "2.9.0",
1712+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
1713+ "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==",
1714+ "license": "MIT",
1715+ "dependencies": {
1716+ "graceful-readlink": ">= 1.0.0"
1717+ },
1718+ "engines": {
1719+ "node": ">= 0.6.x"
1720+ }
1721+ },
1722+ "node_modules/fx-runner/node_modules/isexe": {
1723+ "version": "1.1.2",
1724+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz",
1725+ "integrity": "sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw==",
1726+ "license": "ISC"
1727+ },
1728+ "node_modules/fx-runner/node_modules/which": {
1729+ "version": "1.2.4",
1730+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.4.tgz",
1731+ "integrity": "sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==",
1732+ "license": "ISC",
1733+ "dependencies": {
1734+ "is-absolute": "^0.1.7",
1735+ "isexe": "^1.1.1"
1736+ },
1737+ "bin": {
1738+ "which": "bin/which"
1739+ }
1740+ },
1741+ "node_modules/get-caller-file": {
1742+ "version": "2.0.5",
1743+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
1744+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
1745+ "license": "ISC",
1746+ "engines": {
1747+ "node": "6.* || 8.* || >= 10.*"
1748+ }
1749+ },
1750+ "node_modules/get-east-asian-width": {
1751+ "version": "1.5.0",
1752+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
1753+ "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
1754+ "license": "MIT",
1755+ "engines": {
1756+ "node": ">=18"
1757+ },
1758+ "funding": {
1759+ "url": "https://github.com/sponsors/sindresorhus"
1760+ }
1761+ },
1762+ "node_modules/glob-parent": {
1763+ "version": "6.0.2",
1764+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
1765+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
1766+ "license": "ISC",
1767+ "dependencies": {
1768+ "is-glob": "^4.0.3"
1769+ },
1770+ "engines": {
1771+ "node": ">=10.13.0"
1772+ }
1773+ },
1774+ "node_modules/glob-to-regexp": {
1775+ "version": "0.4.1",
1776+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
1777+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
1778+ "license": "BSD-2-Clause"
1779+ },
1780+ "node_modules/global-directory": {
1781+ "version": "4.0.1",
1782+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
1783+ "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
1784+ "license": "MIT",
1785+ "dependencies": {
1786+ "ini": "4.1.1"
1787+ },
1788+ "engines": {
1789+ "node": ">=18"
1790+ },
1791+ "funding": {
1792+ "url": "https://github.com/sponsors/sindresorhus"
1793+ }
1794+ },
1795+ "node_modules/global-directory/node_modules/ini": {
1796+ "version": "4.1.1",
1797+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
1798+ "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
1799+ "license": "ISC",
1800+ "engines": {
1801+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
1802+ }
1803+ },
1804+ "node_modules/globals": {
1805+ "version": "14.0.0",
1806+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
1807+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
1808+ "license": "MIT",
1809+ "engines": {
1810+ "node": ">=18"
1811+ },
1812+ "funding": {
1813+ "url": "https://github.com/sponsors/sindresorhus"
1814+ }
1815+ },
1816+ "node_modules/graceful-fs": {
1817+ "version": "4.2.11",
1818+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
1819+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
1820+ "license": "ISC"
1821+ },
1822+ "node_modules/graceful-readlink": {
1823+ "version": "1.0.1",
1824+ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
1825+ "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==",
1826+ "license": "MIT"
1827+ },
1828+ "node_modules/growly": {
1829+ "version": "1.3.0",
1830+ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
1831+ "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
1832+ "license": "MIT"
1833+ },
1834+ "node_modules/has-flag": {
1835+ "version": "4.0.0",
1836+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
1837+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
1838+ "license": "MIT",
1839+ "engines": {
1840+ "node": ">=8"
1841+ }
1842+ },
1843+ "node_modules/htmlparser2": {
1844+ "version": "10.1.0",
1845+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
1846+ "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
1847+ "funding": [
1848+ "https://github.com/fb55/htmlparser2?sponsor=1",
1849+ {
1850+ "type": "github",
1851+ "url": "https://github.com/sponsors/fb55"
1852+ }
1853+ ],
1854+ "license": "MIT",
1855+ "dependencies": {
1856+ "domelementtype": "^2.3.0",
1857+ "domhandler": "^5.0.3",
1858+ "domutils": "^3.2.2",
1859+ "entities": "^7.0.1"
1860+ }
1861+ },
1862+ "node_modules/htmlparser2/node_modules/entities": {
1863+ "version": "7.0.1",
1864+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
1865+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
1866+ "license": "BSD-2-Clause",
1867+ "engines": {
1868+ "node": ">=0.12"
1869+ },
1870+ "funding": {
1871+ "url": "https://github.com/fb55/entities?sponsor=1"
1872+ }
1873+ },
1874+ "node_modules/https-proxy-agent": {
1875+ "version": "7.0.6",
1876+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
1877+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
1878+ "license": "MIT",
1879+ "dependencies": {
1880+ "agent-base": "^7.1.2",
1881+ "debug": "4"
1882+ },
1883+ "engines": {
1884+ "node": ">= 14"
1885+ }
1886+ },
1887+ "node_modules/iconv-lite": {
1888+ "version": "0.6.3",
1889+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
1890+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
1891+ "license": "MIT",
1892+ "dependencies": {
1893+ "safer-buffer": ">= 2.1.2 < 3.0.0"
1894+ },
1895+ "engines": {
1896+ "node": ">=0.10.0"
1897+ }
1898+ },
1899+ "node_modules/ignore": {
1900+ "version": "5.3.2",
1901+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
1902+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
1903+ "license": "MIT",
1904+ "engines": {
1905+ "node": ">= 4"
1906+ }
1907+ },
1908+ "node_modules/image-size": {
1909+ "version": "2.0.2",
1910+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz",
1911+ "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==",
1912+ "license": "MIT",
1913+ "bin": {
1914+ "image-size": "bin/image-size.js"
1915+ },
1916+ "engines": {
1917+ "node": ">=16.x"
1918+ }
1919+ },
1920+ "node_modules/immediate": {
1921+ "version": "3.0.6",
1922+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
1923+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
1924+ "license": "MIT"
1925+ },
1926+ "node_modules/import-fresh": {
1927+ "version": "3.3.1",
1928+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
1929+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
1930+ "license": "MIT",
1931+ "dependencies": {
1932+ "parent-module": "^1.0.0",
1933+ "resolve-from": "^4.0.0"
1934+ },
1935+ "engines": {
1936+ "node": ">=6"
1937+ },
1938+ "funding": {
1939+ "url": "https://github.com/sponsors/sindresorhus"
1940+ }
1941+ },
1942+ "node_modules/imurmurhash": {
1943+ "version": "0.1.4",
1944+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
1945+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
1946+ "license": "MIT",
1947+ "engines": {
1948+ "node": ">=0.8.19"
1949+ }
1950+ },
1951+ "node_modules/index-to-position": {
1952+ "version": "1.2.0",
1953+ "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz",
1954+ "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==",
1955+ "license": "MIT",
1956+ "engines": {
1957+ "node": ">=18"
1958+ },
1959+ "funding": {
1960+ "url": "https://github.com/sponsors/sindresorhus"
1961+ }
1962+ },
1963+ "node_modules/inherits": {
1964+ "version": "2.0.4",
1965+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1966+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1967+ "license": "ISC"
1968+ },
1969+ "node_modules/ini": {
1970+ "version": "4.1.3",
1971+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz",
1972+ "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
1973+ "license": "ISC",
1974+ "engines": {
1975+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
1976+ }
1977+ },
1978+ "node_modules/is-absolute": {
1979+ "version": "0.1.7",
1980+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz",
1981+ "integrity": "sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==",
1982+ "license": "MIT",
1983+ "dependencies": {
1984+ "is-relative": "^0.1.0"
1985+ },
1986+ "engines": {
1987+ "node": ">=0.10.0"
1988+ }
1989+ },
1990+ "node_modules/is-docker": {
1991+ "version": "2.2.1",
1992+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
1993+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
1994+ "license": "MIT",
1995+ "bin": {
1996+ "is-docker": "cli.js"
1997+ },
1998+ "engines": {
1999+ "node": ">=8"
2000+ },
2001+ "funding": {
2002+ "url": "https://github.com/sponsors/sindresorhus"
2003+ }
2004+ },
2005+ "node_modules/is-extglob": {
2006+ "version": "2.1.1",
2007+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
2008+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
2009+ "license": "MIT",
2010+ "engines": {
2011+ "node": ">=0.10.0"
2012+ }
2013+ },
2014+ "node_modules/is-fullwidth-code-point": {
2015+ "version": "3.0.0",
2016+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
2017+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
2018+ "license": "MIT",
2019+ "engines": {
2020+ "node": ">=8"
2021+ }
2022+ },
2023+ "node_modules/is-glob": {
2024+ "version": "4.0.3",
2025+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
2026+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
2027+ "license": "MIT",
2028+ "dependencies": {
2029+ "is-extglob": "^2.1.1"
2030+ },
2031+ "engines": {
2032+ "node": ">=0.10.0"
2033+ }
2034+ },
2035+ "node_modules/is-in-ci": {
2036+ "version": "1.0.0",
2037+ "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz",
2038+ "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==",
2039+ "license": "MIT",
2040+ "bin": {
2041+ "is-in-ci": "cli.js"
2042+ },
2043+ "engines": {
2044+ "node": ">=18"
2045+ },
2046+ "funding": {
2047+ "url": "https://github.com/sponsors/sindresorhus"
2048+ }
2049+ },
2050+ "node_modules/is-in-ssh": {
2051+ "version": "1.0.0",
2052+ "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz",
2053+ "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==",
2054+ "license": "MIT",
2055+ "engines": {
2056+ "node": ">=20"
2057+ },
2058+ "funding": {
2059+ "url": "https://github.com/sponsors/sindresorhus"
2060+ }
2061+ },
2062+ "node_modules/is-inside-container": {
2063+ "version": "1.0.0",
2064+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
2065+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
2066+ "license": "MIT",
2067+ "dependencies": {
2068+ "is-docker": "^3.0.0"
2069+ },
2070+ "bin": {
2071+ "is-inside-container": "cli.js"
2072+ },
2073+ "engines": {
2074+ "node": ">=14.16"
2075+ },
2076+ "funding": {
2077+ "url": "https://github.com/sponsors/sindresorhus"
2078+ }
2079+ },
2080+ "node_modules/is-inside-container/node_modules/is-docker": {
2081+ "version": "3.0.0",
2082+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
2083+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
2084+ "license": "MIT",
2085+ "bin": {
2086+ "is-docker": "cli.js"
2087+ },
2088+ "engines": {
2089+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
2090+ },
2091+ "funding": {
2092+ "url": "https://github.com/sponsors/sindresorhus"
2093+ }
2094+ },
2095+ "node_modules/is-installed-globally": {
2096+ "version": "1.0.0",
2097+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz",
2098+ "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==",
2099+ "license": "MIT",
2100+ "dependencies": {
2101+ "global-directory": "^4.0.1",
2102+ "is-path-inside": "^4.0.0"
2103+ },
2104+ "engines": {
2105+ "node": ">=18"
2106+ },
2107+ "funding": {
2108+ "url": "https://github.com/sponsors/sindresorhus"
2109+ }
2110+ },
2111+ "node_modules/is-npm": {
2112+ "version": "6.1.0",
2113+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz",
2114+ "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==",
2115+ "license": "MIT",
2116+ "engines": {
2117+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
2118+ },
2119+ "funding": {
2120+ "url": "https://github.com/sponsors/sindresorhus"
2121+ }
2122+ },
2123+ "node_modules/is-path-inside": {
2124+ "version": "4.0.0",
2125+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz",
2126+ "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
2127+ "license": "MIT",
2128+ "engines": {
2129+ "node": ">=12"
2130+ },
2131+ "funding": {
2132+ "url": "https://github.com/sponsors/sindresorhus"
2133+ }
2134+ },
2135+ "node_modules/is-relative": {
2136+ "version": "0.1.3",
2137+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz",
2138+ "integrity": "sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==",
2139+ "engines": {
2140+ "node": ">=0.10.0"
2141+ }
2142+ },
2143+ "node_modules/is-utf8": {
2144+ "version": "0.2.1",
2145+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
2146+ "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
2147+ "license": "MIT"
2148+ },
2149+ "node_modules/is-wsl": {
2150+ "version": "2.2.0",
2151+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
2152+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
2153+ "license": "MIT",
2154+ "dependencies": {
2155+ "is-docker": "^2.0.0"
2156+ },
2157+ "engines": {
2158+ "node": ">=8"
2159+ }
2160+ },
2161+ "node_modules/isarray": {
2162+ "version": "1.0.0",
2163+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
2164+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
2165+ "license": "MIT"
2166+ },
2167+ "node_modules/isexe": {
2168+ "version": "2.0.0",
2169+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
2170+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
2171+ "license": "ISC"
2172+ },
2173+ "node_modules/jose": {
2174+ "version": "5.9.6",
2175+ "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
2176+ "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
2177+ "license": "MIT",
2178+ "funding": {
2179+ "url": "https://github.com/sponsors/panva"
2180+ }
2181+ },
2182+ "node_modules/js-tokens": {
2183+ "version": "4.0.0",
2184+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
2185+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
2186+ "license": "MIT"
2187+ },
2188+ "node_modules/js-yaml": {
2189+ "version": "4.1.1",
2190+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
2191+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
2192+ "license": "MIT",
2193+ "dependencies": {
2194+ "argparse": "^2.0.1"
2195+ },
2196+ "bin": {
2197+ "js-yaml": "bin/js-yaml.js"
2198+ }
2199+ },
2200+ "node_modules/json-buffer": {
2201+ "version": "3.0.1",
2202+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
2203+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
2204+ "license": "MIT"
2205+ },
2206+ "node_modules/json-merge-patch": {
2207+ "version": "1.0.2",
2208+ "resolved": "https://registry.npmjs.org/json-merge-patch/-/json-merge-patch-1.0.2.tgz",
2209+ "integrity": "sha512-M6Vp2GN9L7cfuMXiWOmHj9bEFbeC250iVtcKQbqVgEsDVYnIsrNsbU+h/Y/PkbBQCtEa4Bez+Ebv0zfbC8ObLg==",
2210+ "license": "MIT",
2211+ "dependencies": {
2212+ "fast-deep-equal": "^3.1.3"
2213+ }
2214+ },
2215+ "node_modules/json-schema-traverse": {
2216+ "version": "1.0.0",
2217+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
2218+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
2219+ "license": "MIT"
2220+ },
2221+ "node_modules/json-stable-stringify-without-jsonify": {
2222+ "version": "1.0.1",
2223+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
2224+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
2225+ "license": "MIT"
2226+ },
2227+ "node_modules/jsonfile": {
2228+ "version": "6.2.0",
2229+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
2230+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
2231+ "license": "MIT",
2232+ "dependencies": {
2233+ "universalify": "^2.0.0"
2234+ },
2235+ "optionalDependencies": {
2236+ "graceful-fs": "^4.1.6"
2237+ }
2238+ },
2239+ "node_modules/jsonwebtoken": {
2240+ "version": "9.0.3",
2241+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
2242+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
2243+ "license": "MIT",
2244+ "dependencies": {
2245+ "jws": "^4.0.1",
2246+ "lodash.includes": "^4.3.0",
2247+ "lodash.isboolean": "^3.0.3",
2248+ "lodash.isinteger": "^4.0.4",
2249+ "lodash.isnumber": "^3.0.3",
2250+ "lodash.isplainobject": "^4.0.6",
2251+ "lodash.isstring": "^4.0.1",
2252+ "lodash.once": "^4.0.0",
2253+ "ms": "^2.1.1",
2254+ "semver": "^7.5.4"
2255+ },
2256+ "engines": {
2257+ "node": ">=12",
2258+ "npm": ">=6"
2259+ }
2260+ },
2261+ "node_modules/jszip": {
2262+ "version": "3.10.1",
2263+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
2264+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
2265+ "license": "(MIT OR GPL-3.0-or-later)",
2266+ "dependencies": {
2267+ "lie": "~3.3.0",
2268+ "pako": "~1.0.2",
2269+ "readable-stream": "~2.3.6",
2270+ "setimmediate": "^1.0.5"
2271+ }
2272+ },
2273+ "node_modules/jwa": {
2274+ "version": "2.0.1",
2275+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
2276+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
2277+ "license": "MIT",
2278+ "dependencies": {
2279+ "buffer-equal-constant-time": "^1.0.1",
2280+ "ecdsa-sig-formatter": "1.0.11",
2281+ "safe-buffer": "^5.0.1"
2282+ }
2283+ },
2284+ "node_modules/jws": {
2285+ "version": "4.0.1",
2286+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
2287+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
2288+ "license": "MIT",
2289+ "dependencies": {
2290+ "jwa": "^2.0.1",
2291+ "safe-buffer": "^5.0.1"
2292+ }
2293+ },
2294+ "node_modules/keyv": {
2295+ "version": "4.5.4",
2296+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
2297+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
2298+ "license": "MIT",
2299+ "dependencies": {
2300+ "json-buffer": "3.0.1"
2301+ }
2302+ },
2303+ "node_modules/ky": {
2304+ "version": "1.14.3",
2305+ "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.3.tgz",
2306+ "integrity": "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==",
2307+ "license": "MIT",
2308+ "engines": {
2309+ "node": ">=18"
2310+ },
2311+ "funding": {
2312+ "url": "https://github.com/sindresorhus/ky?sponsor=1"
2313+ }
2314+ },
2315+ "node_modules/latest-version": {
2316+ "version": "9.0.0",
2317+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-9.0.0.tgz",
2318+ "integrity": "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==",
2319+ "license": "MIT",
2320+ "dependencies": {
2321+ "package-json": "^10.0.0"
2322+ },
2323+ "engines": {
2324+ "node": ">=18"
2325+ },
2326+ "funding": {
2327+ "url": "https://github.com/sponsors/sindresorhus"
2328+ }
2329+ },
2330+ "node_modules/levn": {
2331+ "version": "0.4.1",
2332+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
2333+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
2334+ "license": "MIT",
2335+ "dependencies": {
2336+ "prelude-ls": "^1.2.1",
2337+ "type-check": "~0.4.0"
2338+ },
2339+ "engines": {
2340+ "node": ">= 0.8.0"
2341+ }
2342+ },
2343+ "node_modules/lie": {
2344+ "version": "3.3.0",
2345+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
2346+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
2347+ "license": "MIT",
2348+ "dependencies": {
2349+ "immediate": "~3.0.5"
2350+ }
2351+ },
2352+ "node_modules/lighthouse-logger": {
2353+ "version": "2.0.2",
2354+ "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz",
2355+ "integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==",
2356+ "license": "Apache-2.0",
2357+ "dependencies": {
2358+ "debug": "^4.4.1",
2359+ "marky": "^1.2.2"
2360+ }
2361+ },
2362+ "node_modules/lighthouse-logger/node_modules/debug": {
2363+ "version": "4.4.3",
2364+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
2365+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
2366+ "license": "MIT",
2367+ "dependencies": {
2368+ "ms": "^2.1.3"
2369+ },
2370+ "engines": {
2371+ "node": ">=6.0"
2372+ },
2373+ "peerDependenciesMeta": {
2374+ "supports-color": {
2375+ "optional": true
2376+ }
2377+ }
2378+ },
2379+ "node_modules/locate-path": {
2380+ "version": "6.0.0",
2381+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
2382+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
2383+ "license": "MIT",
2384+ "dependencies": {
2385+ "p-locate": "^5.0.0"
2386+ },
2387+ "engines": {
2388+ "node": ">=10"
2389+ },
2390+ "funding": {
2391+ "url": "https://github.com/sponsors/sindresorhus"
2392+ }
2393+ },
2394+ "node_modules/lodash.includes": {
2395+ "version": "4.3.0",
2396+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
2397+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
2398+ "license": "MIT"
2399+ },
2400+ "node_modules/lodash.isboolean": {
2401+ "version": "3.0.3",
2402+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
2403+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
2404+ "license": "MIT"
2405+ },
2406+ "node_modules/lodash.isinteger": {
2407+ "version": "4.0.4",
2408+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
2409+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
2410+ "license": "MIT"
2411+ },
2412+ "node_modules/lodash.isnumber": {
2413+ "version": "3.0.3",
2414+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
2415+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
2416+ "license": "MIT"
2417+ },
2418+ "node_modules/lodash.isplainobject": {
2419+ "version": "4.0.6",
2420+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
2421+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
2422+ "license": "MIT"
2423+ },
2424+ "node_modules/lodash.isstring": {
2425+ "version": "4.0.1",
2426+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
2427+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
2428+ "license": "MIT"
2429+ },
2430+ "node_modules/lodash.merge": {
2431+ "version": "4.6.2",
2432+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
2433+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
2434+ "license": "MIT"
2435+ },
2436+ "node_modules/lodash.once": {
2437+ "version": "4.1.1",
2438+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
2439+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
2440+ "license": "MIT"
2441+ },
2442+ "node_modules/make-error": {
2443+ "version": "1.3.6",
2444+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
2445+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
2446+ "license": "ISC"
2447+ },
2448+ "node_modules/marky": {
2449+ "version": "1.3.0",
2450+ "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz",
2451+ "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==",
2452+ "license": "Apache-2.0"
2453+ },
2454+ "node_modules/minimatch": {
2455+ "version": "3.1.5",
2456+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
2457+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
2458+ "license": "ISC",
2459+ "dependencies": {
2460+ "brace-expansion": "^1.1.7"
2461+ },
2462+ "engines": {
2463+ "node": "*"
2464+ }
2465+ },
2466+ "node_modules/minimist": {
2467+ "version": "1.2.8",
2468+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
2469+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
2470+ "license": "MIT",
2471+ "funding": {
2472+ "url": "https://github.com/sponsors/ljharb"
2473+ }
2474+ },
2475+ "node_modules/ms": {
2476+ "version": "2.1.3",
2477+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
2478+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
2479+ "license": "MIT"
2480+ },
2481+ "node_modules/multimatch": {
2482+ "version": "6.0.0",
2483+ "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-6.0.0.tgz",
2484+ "integrity": "sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==",
2485+ "license": "MIT",
2486+ "dependencies": {
2487+ "@types/minimatch": "^3.0.5",
2488+ "array-differ": "^4.0.0",
2489+ "array-union": "^3.0.1",
2490+ "minimatch": "^3.0.4"
2491+ },
2492+ "engines": {
2493+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
2494+ },
2495+ "funding": {
2496+ "url": "https://github.com/sponsors/sindresorhus"
2497+ }
2498+ },
2499+ "node_modules/natural-compare": {
2500+ "version": "1.4.0",
2501+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
2502+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
2503+ "license": "MIT"
2504+ },
2505+ "node_modules/node-forge": {
2506+ "version": "1.3.3",
2507+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
2508+ "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
2509+ "license": "(BSD-3-Clause OR GPL-2.0)",
2510+ "engines": {
2511+ "node": ">= 6.13.0"
2512+ }
2513+ },
2514+ "node_modules/node-notifier": {
2515+ "version": "10.0.1",
2516+ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz",
2517+ "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==",
2518+ "license": "MIT",
2519+ "dependencies": {
2520+ "growly": "^1.3.0",
2521+ "is-wsl": "^2.2.0",
2522+ "semver": "^7.3.5",
2523+ "shellwords": "^0.1.1",
2524+ "uuid": "^8.3.2",
2525+ "which": "^2.0.2"
2526+ }
2527+ },
2528+ "node_modules/nth-check": {
2529+ "version": "2.1.1",
2530+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
2531+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
2532+ "license": "BSD-2-Clause",
2533+ "dependencies": {
2534+ "boolbase": "^1.0.0"
2535+ },
2536+ "funding": {
2537+ "url": "https://github.com/fb55/nth-check?sponsor=1"
2538+ }
2539+ },
2540+ "node_modules/on-exit-leak-free": {
2541+ "version": "2.1.2",
2542+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
2543+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
2544+ "license": "MIT",
2545+ "engines": {
2546+ "node": ">=14.0.0"
2547+ }
2548+ },
2549+ "node_modules/open": {
2550+ "version": "11.0.0",
2551+ "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz",
2552+ "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==",
2553+ "license": "MIT",
2554+ "dependencies": {
2555+ "default-browser": "^5.4.0",
2556+ "define-lazy-prop": "^3.0.0",
2557+ "is-in-ssh": "^1.0.0",
2558+ "is-inside-container": "^1.0.0",
2559+ "powershell-utils": "^0.1.0",
2560+ "wsl-utils": "^0.3.0"
2561+ },
2562+ "engines": {
2563+ "node": ">=20"
2564+ },
2565+ "funding": {
2566+ "url": "https://github.com/sponsors/sindresorhus"
2567+ }
2568+ },
2569+ "node_modules/optionator": {
2570+ "version": "0.9.4",
2571+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
2572+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
2573+ "license": "MIT",
2574+ "dependencies": {
2575+ "deep-is": "^0.1.3",
2576+ "fast-levenshtein": "^2.0.6",
2577+ "levn": "^0.4.1",
2578+ "prelude-ls": "^1.2.1",
2579+ "type-check": "^0.4.0",
2580+ "word-wrap": "^1.2.5"
2581+ },
2582+ "engines": {
2583+ "node": ">= 0.8.0"
2584+ }
2585+ },
2586+ "node_modules/os-shim": {
2587+ "version": "0.1.3",
2588+ "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
2589+ "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==",
2590+ "engines": {
2591+ "node": ">= 0.4.0"
2592+ }
2593+ },
2594+ "node_modules/p-limit": {
2595+ "version": "3.1.0",
2596+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
2597+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
2598+ "license": "MIT",
2599+ "dependencies": {
2600+ "yocto-queue": "^0.1.0"
2601+ },
2602+ "engines": {
2603+ "node": ">=10"
2604+ },
2605+ "funding": {
2606+ "url": "https://github.com/sponsors/sindresorhus"
2607+ }
2608+ },
2609+ "node_modules/p-locate": {
2610+ "version": "5.0.0",
2611+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
2612+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
2613+ "license": "MIT",
2614+ "dependencies": {
2615+ "p-limit": "^3.0.2"
2616+ },
2617+ "engines": {
2618+ "node": ">=10"
2619+ },
2620+ "funding": {
2621+ "url": "https://github.com/sponsors/sindresorhus"
2622+ }
2623+ },
2624+ "node_modules/package-json": {
2625+ "version": "10.0.1",
2626+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz",
2627+ "integrity": "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==",
2628+ "license": "MIT",
2629+ "dependencies": {
2630+ "ky": "^1.2.0",
2631+ "registry-auth-token": "^5.0.2",
2632+ "registry-url": "^6.0.1",
2633+ "semver": "^7.6.0"
2634+ },
2635+ "engines": {
2636+ "node": ">=18"
2637+ },
2638+ "funding": {
2639+ "url": "https://github.com/sponsors/sindresorhus"
2640+ }
2641+ },
2642+ "node_modules/pako": {
2643+ "version": "1.0.11",
2644+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
2645+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
2646+ "license": "(MIT AND Zlib)"
2647+ },
2648+ "node_modules/parent-module": {
2649+ "version": "1.0.1",
2650+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
2651+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
2652+ "license": "MIT",
2653+ "dependencies": {
2654+ "callsites": "^3.0.0"
2655+ },
2656+ "engines": {
2657+ "node": ">=6"
2658+ }
2659+ },
2660+ "node_modules/parse-json": {
2661+ "version": "8.3.0",
2662+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
2663+ "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
2664+ "license": "MIT",
2665+ "dependencies": {
2666+ "@babel/code-frame": "^7.26.2",
2667+ "index-to-position": "^1.1.0",
2668+ "type-fest": "^4.39.1"
2669+ },
2670+ "engines": {
2671+ "node": ">=18"
2672+ },
2673+ "funding": {
2674+ "url": "https://github.com/sponsors/sindresorhus"
2675+ }
2676+ },
2677+ "node_modules/parse5": {
2678+ "version": "7.3.0",
2679+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
2680+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
2681+ "license": "MIT",
2682+ "dependencies": {
2683+ "entities": "^6.0.0"
2684+ },
2685+ "funding": {
2686+ "url": "https://github.com/inikulin/parse5?sponsor=1"
2687+ }
2688+ },
2689+ "node_modules/parse5-htmlparser2-tree-adapter": {
2690+ "version": "7.1.0",
2691+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
2692+ "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
2693+ "license": "MIT",
2694+ "dependencies": {
2695+ "domhandler": "^5.0.3",
2696+ "parse5": "^7.0.0"
2697+ },
2698+ "funding": {
2699+ "url": "https://github.com/inikulin/parse5?sponsor=1"
2700+ }
2701+ },
2702+ "node_modules/parse5-parser-stream": {
2703+ "version": "7.1.2",
2704+ "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
2705+ "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
2706+ "license": "MIT",
2707+ "dependencies": {
2708+ "parse5": "^7.0.0"
2709+ },
2710+ "funding": {
2711+ "url": "https://github.com/inikulin/parse5?sponsor=1"
2712+ }
2713+ },
2714+ "node_modules/parse5/node_modules/entities": {
2715+ "version": "6.0.1",
2716+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
2717+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
2718+ "license": "BSD-2-Clause",
2719+ "engines": {
2720+ "node": ">=0.12"
2721+ },
2722+ "funding": {
2723+ "url": "https://github.com/fb55/entities?sponsor=1"
2724+ }
2725+ },
2726+ "node_modules/path-exists": {
2727+ "version": "4.0.0",
2728+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
2729+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
2730+ "license": "MIT",
2731+ "engines": {
2732+ "node": ">=8"
2733+ }
2734+ },
2735+ "node_modules/path-key": {
2736+ "version": "3.1.1",
2737+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
2738+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
2739+ "license": "MIT",
2740+ "engines": {
2741+ "node": ">=8"
2742+ }
2743+ },
2744+ "node_modules/pend": {
2745+ "version": "1.2.0",
2746+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
2747+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
2748+ "license": "MIT"
2749+ },
2750+ "node_modules/picocolors": {
2751+ "version": "1.1.1",
2752+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
2753+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
2754+ "license": "ISC"
2755+ },
2756+ "node_modules/pino": {
2757+ "version": "10.3.1",
2758+ "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
2759+ "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
2760+ "license": "MIT",
2761+ "dependencies": {
2762+ "@pinojs/redact": "^0.4.0",
2763+ "atomic-sleep": "^1.0.0",
2764+ "on-exit-leak-free": "^2.1.0",
2765+ "pino-abstract-transport": "^3.0.0",
2766+ "pino-std-serializers": "^7.0.0",
2767+ "process-warning": "^5.0.0",
2768+ "quick-format-unescaped": "^4.0.3",
2769+ "real-require": "^0.2.0",
2770+ "safe-stable-stringify": "^2.3.1",
2771+ "sonic-boom": "^4.0.1",
2772+ "thread-stream": "^4.0.0"
2773+ },
2774+ "bin": {
2775+ "pino": "bin.js"
2776+ }
2777+ },
2778+ "node_modules/pino-abstract-transport": {
2779+ "version": "3.0.0",
2780+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
2781+ "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
2782+ "license": "MIT",
2783+ "dependencies": {
2784+ "split2": "^4.0.0"
2785+ }
2786+ },
2787+ "node_modules/pino-std-serializers": {
2788+ "version": "7.1.0",
2789+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
2790+ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
2791+ "license": "MIT"
2792+ },
2793+ "node_modules/powershell-utils": {
2794+ "version": "0.1.0",
2795+ "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz",
2796+ "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==",
2797+ "license": "MIT",
2798+ "engines": {
2799+ "node": ">=20"
2800+ },
2801+ "funding": {
2802+ "url": "https://github.com/sponsors/sindresorhus"
2803+ }
2804+ },
2805+ "node_modules/prelude-ls": {
2806+ "version": "1.2.1",
2807+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
2808+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
2809+ "license": "MIT",
2810+ "engines": {
2811+ "node": ">= 0.8.0"
2812+ }
2813+ },
2814+ "node_modules/process-nextick-args": {
2815+ "version": "2.0.1",
2816+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
2817+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
2818+ "license": "MIT"
2819+ },
2820+ "node_modules/process-warning": {
2821+ "version": "5.0.0",
2822+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
2823+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
2824+ "funding": [
2825+ {
2826+ "type": "github",
2827+ "url": "https://github.com/sponsors/fastify"
2828+ },
2829+ {
2830+ "type": "opencollective",
2831+ "url": "https://opencollective.com/fastify"
2832+ }
2833+ ],
2834+ "license": "MIT"
2835+ },
2836+ "node_modules/promise-toolbox": {
2837+ "version": "0.21.0",
2838+ "resolved": "https://registry.npmjs.org/promise-toolbox/-/promise-toolbox-0.21.0.tgz",
2839+ "integrity": "sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==",
2840+ "license": "ISC",
2841+ "dependencies": {
2842+ "make-error": "^1.3.2"
2843+ },
2844+ "engines": {
2845+ "node": ">=6"
2846+ }
2847+ },
2848+ "node_modules/proto-list": {
2849+ "version": "1.2.4",
2850+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
2851+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
2852+ "license": "ISC"
2853+ },
2854+ "node_modules/punycode": {
2855+ "version": "2.3.1",
2856+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
2857+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
2858+ "license": "MIT",
2859+ "engines": {
2860+ "node": ">=6"
2861+ }
2862+ },
2863+ "node_modules/pupa": {
2864+ "version": "3.3.0",
2865+ "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz",
2866+ "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==",
2867+ "license": "MIT",
2868+ "dependencies": {
2869+ "escape-goat": "^4.0.0"
2870+ },
2871+ "engines": {
2872+ "node": ">=12.20"
2873+ },
2874+ "funding": {
2875+ "url": "https://github.com/sponsors/sindresorhus"
2876+ }
2877+ },
2878+ "node_modules/quick-format-unescaped": {
2879+ "version": "4.0.4",
2880+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
2881+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
2882+ "license": "MIT"
2883+ },
2884+ "node_modules/rc": {
2885+ "version": "1.2.8",
2886+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
2887+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
2888+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
2889+ "dependencies": {
2890+ "deep-extend": "^0.6.0",
2891+ "ini": "~1.3.0",
2892+ "minimist": "^1.2.0",
2893+ "strip-json-comments": "~2.0.1"
2894+ },
2895+ "bin": {
2896+ "rc": "cli.js"
2897+ }
2898+ },
2899+ "node_modules/rc/node_modules/ini": {
2900+ "version": "1.3.8",
2901+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
2902+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
2903+ "license": "ISC"
2904+ },
2905+ "node_modules/rc/node_modules/strip-json-comments": {
2906+ "version": "2.0.1",
2907+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
2908+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
2909+ "license": "MIT",
2910+ "engines": {
2911+ "node": ">=0.10.0"
2912+ }
2913+ },
2914+ "node_modules/readable-stream": {
2915+ "version": "2.3.8",
2916+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
2917+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
2918+ "license": "MIT",
2919+ "dependencies": {
2920+ "core-util-is": "~1.0.0",
2921+ "inherits": "~2.0.3",
2922+ "isarray": "~1.0.0",
2923+ "process-nextick-args": "~2.0.0",
2924+ "safe-buffer": "~5.1.1",
2925+ "string_decoder": "~1.1.1",
2926+ "util-deprecate": "~1.0.1"
2927+ }
2928+ },
2929+ "node_modules/readable-stream/node_modules/safe-buffer": {
2930+ "version": "5.1.2",
2931+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
2932+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
2933+ "license": "MIT"
2934+ },
2935+ "node_modules/real-require": {
2936+ "version": "0.2.0",
2937+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
2938+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
2939+ "license": "MIT",
2940+ "engines": {
2941+ "node": ">= 12.13.0"
2942+ }
2943+ },
2944+ "node_modules/registry-auth-token": {
2945+ "version": "5.1.1",
2946+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz",
2947+ "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==",
2948+ "license": "MIT",
2949+ "dependencies": {
2950+ "@pnpm/npm-conf": "^3.0.2"
2951+ },
2952+ "engines": {
2953+ "node": ">=14"
2954+ }
2955+ },
2956+ "node_modules/registry-url": {
2957+ "version": "6.0.1",
2958+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
2959+ "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
2960+ "license": "MIT",
2961+ "dependencies": {
2962+ "rc": "1.2.8"
2963+ },
2964+ "engines": {
2965+ "node": ">=12"
2966+ },
2967+ "funding": {
2968+ "url": "https://github.com/sponsors/sindresorhus"
2969+ }
2970+ },
2971+ "node_modules/require-directory": {
2972+ "version": "2.1.1",
2973+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
2974+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
2975+ "license": "MIT",
2976+ "engines": {
2977+ "node": ">=0.10.0"
2978+ }
2979+ },
2980+ "node_modules/require-from-string": {
2981+ "version": "2.0.2",
2982+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
2983+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
2984+ "license": "MIT",
2985+ "engines": {
2986+ "node": ">=0.10.0"
2987+ }
2988+ },
2989+ "node_modules/resolve-from": {
2990+ "version": "4.0.0",
2991+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
2992+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
2993+ "license": "MIT",
2994+ "engines": {
2995+ "node": ">=4"
2996+ }
2997+ },
2998+ "node_modules/run-applescript": {
2999+ "version": "7.1.0",
3000+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
3001+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
3002+ "license": "MIT",
3003+ "engines": {
3004+ "node": ">=18"
3005+ },
3006+ "funding": {
3007+ "url": "https://github.com/sponsors/sindresorhus"
3008+ }
3009+ },
3010+ "node_modules/safe-buffer": {
3011+ "version": "5.2.1",
3012+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
3013+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
3014+ "funding": [
3015+ {
3016+ "type": "github",
3017+ "url": "https://github.com/sponsors/feross"
3018+ },
3019+ {
3020+ "type": "patreon",
3021+ "url": "https://www.patreon.com/feross"
3022+ },
3023+ {
3024+ "type": "consulting",
3025+ "url": "https://feross.org/support"
3026+ }
3027+ ],
3028+ "license": "MIT"
3029+ },
3030+ "node_modules/safe-stable-stringify": {
3031+ "version": "2.5.0",
3032+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
3033+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
3034+ "license": "MIT",
3035+ "engines": {
3036+ "node": ">=10"
3037+ }
3038+ },
3039+ "node_modules/safer-buffer": {
3040+ "version": "2.1.2",
3041+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
3042+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
3043+ "license": "MIT"
3044+ },
3045+ "node_modules/sax": {
3046+ "version": "1.6.0",
3047+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
3048+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
3049+ "license": "BlueOak-1.0.0",
3050+ "engines": {
3051+ "node": ">=11.0.0"
3052+ }
3053+ },
3054+ "node_modules/semver": {
3055+ "version": "7.7.4",
3056+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
3057+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
3058+ "license": "ISC",
3059+ "bin": {
3060+ "semver": "bin/semver.js"
3061+ },
3062+ "engines": {
3063+ "node": ">=10"
3064+ }
3065+ },
3066+ "node_modules/setimmediate": {
3067+ "version": "1.0.5",
3068+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
3069+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
3070+ "license": "MIT"
3071+ },
3072+ "node_modules/shebang-command": {
3073+ "version": "2.0.0",
3074+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
3075+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
3076+ "license": "MIT",
3077+ "dependencies": {
3078+ "shebang-regex": "^3.0.0"
3079+ },
3080+ "engines": {
3081+ "node": ">=8"
3082+ }
3083+ },
3084+ "node_modules/shebang-regex": {
3085+ "version": "3.0.0",
3086+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
3087+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
3088+ "license": "MIT",
3089+ "engines": {
3090+ "node": ">=8"
3091+ }
3092+ },
3093+ "node_modules/shell-quote": {
3094+ "version": "1.7.3",
3095+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
3096+ "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
3097+ "license": "MIT"
3098+ },
3099+ "node_modules/shellwords": {
3100+ "version": "0.1.1",
3101+ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
3102+ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
3103+ "license": "MIT"
3104+ },
3105+ "node_modules/sonic-boom": {
3106+ "version": "4.2.1",
3107+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
3108+ "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
3109+ "license": "MIT",
3110+ "dependencies": {
3111+ "atomic-sleep": "^1.0.0"
3112+ }
3113+ },
3114+ "node_modules/source-map": {
3115+ "version": "0.6.1",
3116+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
3117+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
3118+ "license": "BSD-3-Clause",
3119+ "engines": {
3120+ "node": ">=0.10.0"
3121+ }
3122+ },
3123+ "node_modules/source-map-support": {
3124+ "version": "0.5.21",
3125+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
3126+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
3127+ "license": "MIT",
3128+ "dependencies": {
3129+ "buffer-from": "^1.0.0",
3130+ "source-map": "^0.6.0"
3131+ }
3132+ },
3133+ "node_modules/spawn-sync": {
3134+ "version": "1.0.15",
3135+ "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
3136+ "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==",
3137+ "hasInstallScript": true,
3138+ "license": "MIT",
3139+ "dependencies": {
3140+ "concat-stream": "^1.4.7",
3141+ "os-shim": "^0.1.2"
3142+ }
3143+ },
3144+ "node_modules/split": {
3145+ "version": "1.0.1",
3146+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
3147+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
3148+ "license": "MIT",
3149+ "dependencies": {
3150+ "through": "2"
3151+ },
3152+ "engines": {
3153+ "node": "*"
3154+ }
3155+ },
3156+ "node_modules/split2": {
3157+ "version": "4.2.0",
3158+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
3159+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
3160+ "license": "ISC",
3161+ "engines": {
3162+ "node": ">= 10.x"
3163+ }
3164+ },
3165+ "node_modules/string_decoder": {
3166+ "version": "1.1.1",
3167+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
3168+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
3169+ "license": "MIT",
3170+ "dependencies": {
3171+ "safe-buffer": "~5.1.0"
3172+ }
3173+ },
3174+ "node_modules/string_decoder/node_modules/safe-buffer": {
3175+ "version": "5.1.2",
3176+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
3177+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
3178+ "license": "MIT"
3179+ },
3180+ "node_modules/string-width": {
3181+ "version": "7.2.0",
3182+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
3183+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
3184+ "license": "MIT",
3185+ "dependencies": {
3186+ "emoji-regex": "^10.3.0",
3187+ "get-east-asian-width": "^1.0.0",
3188+ "strip-ansi": "^7.1.0"
3189+ },
3190+ "engines": {
3191+ "node": ">=18"
3192+ },
3193+ "funding": {
3194+ "url": "https://github.com/sponsors/sindresorhus"
3195+ }
3196+ },
3197+ "node_modules/string-width/node_modules/ansi-regex": {
3198+ "version": "6.2.2",
3199+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
3200+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
3201+ "license": "MIT",
3202+ "engines": {
3203+ "node": ">=12"
3204+ },
3205+ "funding": {
3206+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
3207+ }
3208+ },
3209+ "node_modules/string-width/node_modules/strip-ansi": {
3210+ "version": "7.2.0",
3211+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
3212+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
3213+ "license": "MIT",
3214+ "dependencies": {
3215+ "ansi-regex": "^6.2.2"
3216+ },
3217+ "engines": {
3218+ "node": ">=12"
3219+ },
3220+ "funding": {
3221+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
3222+ }
3223+ },
3224+ "node_modules/strip-ansi": {
3225+ "version": "6.0.1",
3226+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
3227+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
3228+ "license": "MIT",
3229+ "dependencies": {
3230+ "ansi-regex": "^5.0.1"
3231+ },
3232+ "engines": {
3233+ "node": ">=8"
3234+ }
3235+ },
3236+ "node_modules/strip-bom": {
3237+ "version": "5.0.0",
3238+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-5.0.0.tgz",
3239+ "integrity": "sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==",
3240+ "license": "MIT",
3241+ "engines": {
3242+ "node": ">=12"
3243+ },
3244+ "funding": {
3245+ "url": "https://github.com/sponsors/sindresorhus"
3246+ }
3247+ },
3248+ "node_modules/strip-bom-buf": {
3249+ "version": "2.0.0",
3250+ "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz",
3251+ "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==",
3252+ "license": "MIT",
3253+ "dependencies": {
3254+ "is-utf8": "^0.2.1"
3255+ },
3256+ "engines": {
3257+ "node": ">=8"
3258+ }
3259+ },
3260+ "node_modules/strip-bom-stream": {
3261+ "version": "4.0.0",
3262+ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz",
3263+ "integrity": "sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==",
3264+ "license": "MIT",
3265+ "dependencies": {
3266+ "first-chunk-stream": "^3.0.0",
3267+ "strip-bom-buf": "^2.0.0"
3268+ },
3269+ "engines": {
3270+ "node": ">=8"
3271+ }
3272+ },
3273+ "node_modules/strip-json-comments": {
3274+ "version": "5.0.3",
3275+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
3276+ "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
3277+ "license": "MIT",
3278+ "engines": {
3279+ "node": ">=14.16"
3280+ },
3281+ "funding": {
3282+ "url": "https://github.com/sponsors/sindresorhus"
3283+ }
3284+ },
3285+ "node_modules/stubborn-fs": {
3286+ "version": "2.0.0",
3287+ "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz",
3288+ "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==",
3289+ "license": "MIT",
3290+ "dependencies": {
3291+ "stubborn-utils": "^1.0.1"
3292+ }
3293+ },
3294+ "node_modules/stubborn-utils": {
3295+ "version": "1.0.2",
3296+ "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz",
3297+ "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==",
3298+ "license": "MIT"
3299+ },
3300+ "node_modules/supports-color": {
3301+ "version": "7.2.0",
3302+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
3303+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
3304+ "license": "MIT",
3305+ "dependencies": {
3306+ "has-flag": "^4.0.0"
3307+ },
3308+ "engines": {
3309+ "node": ">=8"
3310+ }
3311+ },
3312+ "node_modules/thread-stream": {
3313+ "version": "4.0.0",
3314+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz",
3315+ "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==",
3316+ "license": "MIT",
3317+ "dependencies": {
3318+ "real-require": "^0.2.0"
3319+ },
3320+ "engines": {
3321+ "node": ">=20"
3322+ }
3323+ },
3324+ "node_modules/through": {
3325+ "version": "2.3.8",
3326+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
3327+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
3328+ "license": "MIT"
3329+ },
3330+ "node_modules/tmp": {
3331+ "version": "0.2.5",
3332+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
3333+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
3334+ "license": "MIT",
3335+ "engines": {
3336+ "node": ">=14.14"
3337+ }
3338+ },
3339+ "node_modules/type-check": {
3340+ "version": "0.4.0",
3341+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
3342+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
3343+ "license": "MIT",
3344+ "dependencies": {
3345+ "prelude-ls": "^1.2.1"
3346+ },
3347+ "engines": {
3348+ "node": ">= 0.8.0"
3349+ }
3350+ },
3351+ "node_modules/type-fest": {
3352+ "version": "4.41.0",
3353+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
3354+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
3355+ "license": "(MIT OR CC0-1.0)",
3356+ "engines": {
3357+ "node": ">=16"
3358+ },
3359+ "funding": {
3360+ "url": "https://github.com/sponsors/sindresorhus"
3361+ }
3362+ },
3363+ "node_modules/typedarray": {
3364+ "version": "0.0.6",
3365+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
3366+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
3367+ "license": "MIT"
3368+ },
3369+ "node_modules/undici": {
3370+ "version": "7.24.4",
3371+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz",
3372+ "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==",
3373+ "license": "MIT",
3374+ "engines": {
3375+ "node": ">=20.18.1"
3376+ }
3377+ },
3378+ "node_modules/undici-types": {
3379+ "version": "7.18.2",
3380+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
3381+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
3382+ "license": "MIT"
3383+ },
3384+ "node_modules/universalify": {
3385+ "version": "2.0.1",
3386+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
3387+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
3388+ "license": "MIT",
3389+ "engines": {
3390+ "node": ">= 10.0.0"
3391+ }
3392+ },
3393+ "node_modules/upath": {
3394+ "version": "2.0.1",
3395+ "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
3396+ "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
3397+ "license": "MIT",
3398+ "engines": {
3399+ "node": ">=4",
3400+ "yarn": "*"
3401+ }
3402+ },
3403+ "node_modules/update-notifier": {
3404+ "version": "7.3.1",
3405+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz",
3406+ "integrity": "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==",
3407+ "license": "BSD-2-Clause",
3408+ "dependencies": {
3409+ "boxen": "^8.0.1",
3410+ "chalk": "^5.3.0",
3411+ "configstore": "^7.0.0",
3412+ "is-in-ci": "^1.0.0",
3413+ "is-installed-globally": "^1.0.0",
3414+ "is-npm": "^6.0.0",
3415+ "latest-version": "^9.0.0",
3416+ "pupa": "^3.1.0",
3417+ "semver": "^7.6.3",
3418+ "xdg-basedir": "^5.1.0"
3419+ },
3420+ "engines": {
3421+ "node": ">=18"
3422+ },
3423+ "funding": {
3424+ "url": "https://github.com/yeoman/update-notifier?sponsor=1"
3425+ }
3426+ },
3427+ "node_modules/update-notifier/node_modules/chalk": {
3428+ "version": "5.6.2",
3429+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
3430+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
3431+ "license": "MIT",
3432+ "engines": {
3433+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
3434+ },
3435+ "funding": {
3436+ "url": "https://github.com/chalk/chalk?sponsor=1"
3437+ }
3438+ },
3439+ "node_modules/uri-js": {
3440+ "version": "4.4.1",
3441+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
3442+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
3443+ "license": "BSD-2-Clause",
3444+ "dependencies": {
3445+ "punycode": "^2.1.0"
3446+ }
3447+ },
3448+ "node_modules/util-deprecate": {
3449+ "version": "1.0.2",
3450+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
3451+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
3452+ "license": "MIT"
3453+ },
3454+ "node_modules/uuid": {
3455+ "version": "8.3.2",
3456+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
3457+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
3458+ "license": "MIT",
3459+ "bin": {
3460+ "uuid": "dist/bin/uuid"
3461+ }
3462+ },
3463+ "node_modules/watchpack": {
3464+ "version": "2.5.1",
3465+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
3466+ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
3467+ "license": "MIT",
3468+ "dependencies": {
3469+ "glob-to-regexp": "^0.4.1",
3470+ "graceful-fs": "^4.1.2"
3471+ },
3472+ "engines": {
3473+ "node": ">=10.13.0"
3474+ }
3475+ },
3476+ "node_modules/wcwidth": {
3477+ "version": "1.0.1",
3478+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
3479+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
3480+ "license": "MIT",
3481+ "dependencies": {
3482+ "defaults": "^1.0.3"
3483+ }
3484+ },
3485+ "node_modules/web-ext": {
3486+ "version": "10.0.0",
3487+ "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-10.0.0.tgz",
3488+ "integrity": "sha512-Cdr7GELRVOXGdV8NqZAPa9oImXWUMDbzYff+UBx5F6mz4zfIrnF3+iYUsvBfG9VJ/VsqmXKdpt1gBsXbH8Z75A==",
3489+ "license": "MPL-2.0",
3490+ "dependencies": {
3491+ "@babel/runtime": "7.28.6",
3492+ "@devicefarmer/adbkit": "3.3.8",
3493+ "addons-linter": "10.1.0",
3494+ "camelcase": "8.0.0",
3495+ "chrome-launcher": "1.2.0",
3496+ "debounce": "1.2.1",
3497+ "decamelize": "6.0.1",
3498+ "es6-error": "4.1.1",
3499+ "firefox-profile": "4.7.0",
3500+ "fx-runner": "1.4.0",
3501+ "https-proxy-agent": "^7.0.0",
3502+ "jose": "5.9.6",
3503+ "jszip": "3.10.1",
3504+ "multimatch": "6.0.0",
3505+ "node-notifier": "10.0.1",
3506+ "open": "11.0.0",
3507+ "parse-json": "8.3.0",
3508+ "pino": "10.3.1",
3509+ "promise-toolbox": "0.21.0",
3510+ "source-map-support": "0.5.21",
3511+ "strip-bom": "5.0.0",
3512+ "strip-json-comments": "5.0.3",
3513+ "tmp": "0.2.5",
3514+ "update-notifier": "7.3.1",
3515+ "watchpack": "2.5.1",
3516+ "yargs": "17.7.2",
3517+ "zip-dir": "2.0.0"
3518+ },
3519+ "bin": {
3520+ "web-ext": "bin/web-ext.js"
3521+ },
3522+ "engines": {
3523+ "node": ">=20.0.0",
3524+ "npm": ">=8.0.0"
3525+ }
3526+ },
3527+ "node_modules/whatwg-encoding": {
3528+ "version": "3.1.1",
3529+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
3530+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
3531+ "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
3532+ "license": "MIT",
3533+ "dependencies": {
3534+ "iconv-lite": "0.6.3"
3535+ },
3536+ "engines": {
3537+ "node": ">=18"
3538+ }
3539+ },
3540+ "node_modules/whatwg-mimetype": {
3541+ "version": "4.0.0",
3542+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
3543+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
3544+ "license": "MIT",
3545+ "engines": {
3546+ "node": ">=18"
3547+ }
3548+ },
3549+ "node_modules/when": {
3550+ "version": "3.7.7",
3551+ "resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz",
3552+ "integrity": "sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==",
3553+ "license": "MIT"
3554+ },
3555+ "node_modules/when-exit": {
3556+ "version": "2.1.5",
3557+ "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz",
3558+ "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==",
3559+ "license": "MIT"
3560+ },
3561+ "node_modules/which": {
3562+ "version": "2.0.2",
3563+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
3564+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
3565+ "license": "ISC",
3566+ "dependencies": {
3567+ "isexe": "^2.0.0"
3568+ },
3569+ "bin": {
3570+ "node-which": "bin/node-which"
3571+ },
3572+ "engines": {
3573+ "node": ">= 8"
3574+ }
3575+ },
3576+ "node_modules/widest-line": {
3577+ "version": "5.0.0",
3578+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
3579+ "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
3580+ "license": "MIT",
3581+ "dependencies": {
3582+ "string-width": "^7.0.0"
3583+ },
3584+ "engines": {
3585+ "node": ">=18"
3586+ },
3587+ "funding": {
3588+ "url": "https://github.com/sponsors/sindresorhus"
3589+ }
3590+ },
3591+ "node_modules/winreg": {
3592+ "version": "0.0.12",
3593+ "resolved": "https://registry.npmjs.org/winreg/-/winreg-0.0.12.tgz",
3594+ "integrity": "sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==",
3595+ "license": "BSD"
3596+ },
3597+ "node_modules/word-wrap": {
3598+ "version": "1.2.5",
3599+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
3600+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
3601+ "license": "MIT",
3602+ "engines": {
3603+ "node": ">=0.10.0"
3604+ }
3605+ },
3606+ "node_modules/wrap-ansi": {
3607+ "version": "9.0.2",
3608+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
3609+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
3610+ "license": "MIT",
3611+ "dependencies": {
3612+ "ansi-styles": "^6.2.1",
3613+ "string-width": "^7.0.0",
3614+ "strip-ansi": "^7.1.0"
3615+ },
3616+ "engines": {
3617+ "node": ">=18"
3618+ },
3619+ "funding": {
3620+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
3621+ }
3622+ },
3623+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
3624+ "version": "6.2.2",
3625+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
3626+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
3627+ "license": "MIT",
3628+ "engines": {
3629+ "node": ">=12"
3630+ },
3631+ "funding": {
3632+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
3633+ }
3634+ },
3635+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
3636+ "version": "6.2.3",
3637+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
3638+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
3639+ "license": "MIT",
3640+ "engines": {
3641+ "node": ">=12"
3642+ },
3643+ "funding": {
3644+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
3645+ }
3646+ },
3647+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
3648+ "version": "7.2.0",
3649+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
3650+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
3651+ "license": "MIT",
3652+ "dependencies": {
3653+ "ansi-regex": "^6.2.2"
3654+ },
3655+ "engines": {
3656+ "node": ">=12"
3657+ },
3658+ "funding": {
3659+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
3660+ }
3661+ },
3662+ "node_modules/wsl-utils": {
3663+ "version": "0.3.1",
3664+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz",
3665+ "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==",
3666+ "license": "MIT",
3667+ "dependencies": {
3668+ "is-wsl": "^3.1.0",
3669+ "powershell-utils": "^0.1.0"
3670+ },
3671+ "engines": {
3672+ "node": ">=20"
3673+ },
3674+ "funding": {
3675+ "url": "https://github.com/sponsors/sindresorhus"
3676+ }
3677+ },
3678+ "node_modules/wsl-utils/node_modules/is-wsl": {
3679+ "version": "3.1.1",
3680+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
3681+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
3682+ "license": "MIT",
3683+ "dependencies": {
3684+ "is-inside-container": "^1.0.0"
3685+ },
3686+ "engines": {
3687+ "node": ">=16"
3688+ },
3689+ "funding": {
3690+ "url": "https://github.com/sponsors/sindresorhus"
3691+ }
3692+ },
3693+ "node_modules/xdg-basedir": {
3694+ "version": "5.1.0",
3695+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
3696+ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
3697+ "license": "MIT",
3698+ "engines": {
3699+ "node": ">=12"
3700+ },
3701+ "funding": {
3702+ "url": "https://github.com/sponsors/sindresorhus"
3703+ }
3704+ },
3705+ "node_modules/xml2js": {
3706+ "version": "0.6.2",
3707+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
3708+ "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
3709+ "license": "MIT",
3710+ "dependencies": {
3711+ "sax": ">=0.6.0",
3712+ "xmlbuilder": "~11.0.0"
3713+ },
3714+ "engines": {
3715+ "node": ">=4.0.0"
3716+ }
3717+ },
3718+ "node_modules/xmlbuilder": {
3719+ "version": "11.0.1",
3720+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
3721+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
3722+ "license": "MIT",
3723+ "engines": {
3724+ "node": ">=4.0"
3725+ }
3726+ },
3727+ "node_modules/y18n": {
3728+ "version": "5.0.8",
3729+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
3730+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
3731+ "license": "ISC",
3732+ "engines": {
3733+ "node": ">=10"
3734+ }
3735+ },
3736+ "node_modules/yargs": {
3737+ "version": "17.7.2",
3738+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
3739+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
3740+ "license": "MIT",
3741+ "dependencies": {
3742+ "cliui": "^8.0.1",
3743+ "escalade": "^3.1.1",
3744+ "get-caller-file": "^2.0.5",
3745+ "require-directory": "^2.1.1",
3746+ "string-width": "^4.2.3",
3747+ "y18n": "^5.0.5",
3748+ "yargs-parser": "^21.1.1"
3749+ },
3750+ "engines": {
3751+ "node": ">=12"
3752+ }
3753+ },
3754+ "node_modules/yargs-parser": {
3755+ "version": "21.1.1",
3756+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
3757+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
3758+ "license": "ISC",
3759+ "engines": {
3760+ "node": ">=12"
3761+ }
3762+ },
3763+ "node_modules/yargs/node_modules/emoji-regex": {
3764+ "version": "8.0.0",
3765+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
3766+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
3767+ "license": "MIT"
3768+ },
3769+ "node_modules/yargs/node_modules/string-width": {
3770+ "version": "4.2.3",
3771+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
3772+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
3773+ "license": "MIT",
3774+ "dependencies": {
3775+ "emoji-regex": "^8.0.0",
3776+ "is-fullwidth-code-point": "^3.0.0",
3777+ "strip-ansi": "^6.0.1"
3778+ },
3779+ "engines": {
3780+ "node": ">=8"
3781+ }
3782+ },
3783+ "node_modules/yauzl": {
3784+ "version": "3.2.1",
3785+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.1.tgz",
3786+ "integrity": "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A==",
3787+ "license": "MIT",
3788+ "dependencies": {
3789+ "buffer-crc32": "~0.2.3",
3790+ "pend": "~1.2.0"
3791+ },
3792+ "engines": {
3793+ "node": ">=12"
3794+ }
3795+ },
3796+ "node_modules/yocto-queue": {
3797+ "version": "0.1.0",
3798+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
3799+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
3800+ "license": "MIT",
3801+ "engines": {
3802+ "node": ">=10"
3803+ },
3804+ "funding": {
3805+ "url": "https://github.com/sponsors/sindresorhus"
3806+ }
3807+ },
3808+ "node_modules/zip-dir": {
3809+ "version": "2.0.0",
3810+ "resolved": "https://registry.npmjs.org/zip-dir/-/zip-dir-2.0.0.tgz",
3811+ "integrity": "sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==",
3812+ "license": "MIT",
3813+ "dependencies": {
3814+ "async": "^3.2.0",
3815+ "jszip": "^3.2.2"
3816+ }
3817+ }
3818+ }
3819+}
+10,
-0
1@@ -0,0 +1,10 @@
2+{
3+ "name": "baa-firefox",
4+ "private": true,
5+ "scripts": {
6+ "run:persistent": "bash ./scripts/run-persistent.sh"
7+ },
8+ "dependencies": {
9+ "web-ext": "^10.0.0"
10+ }
11+}
+555,
-0
1@@ -0,0 +1,555 @@
2+(function () {
3+ if (window.__baaFirefoxIntercepted__) return;
4+ window.__baaFirefoxIntercepted__ = true;
5+
6+ const BODY_LIMIT = 5000;
7+ const originalFetch = window.fetch;
8+ const originalXhrOpen = XMLHttpRequest.prototype.open;
9+ const originalXhrSend = XMLHttpRequest.prototype.send;
10+ const originalXhrSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
11+
12+ function hostnameMatches(hostname, hosts) {
13+ return hosts.some((host) => hostname === host || hostname.endsWith(`.${host}`));
14+ }
15+
16+ function isLikelyStaticPath(pathname = "") {
17+ const lower = pathname.toLowerCase();
18+ return lower.startsWith("/_next/")
19+ || lower.startsWith("/assets/")
20+ || lower.startsWith("/static/")
21+ || lower.startsWith("/images/")
22+ || lower.startsWith("/fonts/")
23+ || lower.startsWith("/favicon")
24+ || /\.(?:js|mjs|css|map|png|jpe?g|gif|svg|webp|ico|woff2?|ttf|otf|mp4|webm|txt)$/i.test(lower);
25+ }
26+
27+ const PLATFORM_RULES = [
28+ {
29+ platform: "claude",
30+ pageHosts: ["claude.ai"],
31+ requestHosts: ["claude.ai"],
32+ matchesPageHost(hostname) {
33+ return hostnameMatches(hostname, this.pageHosts);
34+ },
35+ matchesRequestHost(hostname) {
36+ return hostnameMatches(hostname, this.requestHosts);
37+ },
38+ shouldTrack(pathname) {
39+ return pathname.includes("/api/");
40+ },
41+ isSse(pathname, contentType) {
42+ return contentType.includes("text/event-stream") || pathname.endsWith("/completion");
43+ }
44+ },
45+ {
46+ platform: "chatgpt",
47+ pageHosts: ["chatgpt.com", "chat.openai.com"],
48+ requestHosts: ["chatgpt.com", "chat.openai.com", "openai.com", "oaiusercontent.com"],
49+ matchesPageHost(hostname) {
50+ return hostnameMatches(hostname, this.pageHosts);
51+ },
52+ matchesRequestHost(hostname) {
53+ return hostnameMatches(hostname, this.requestHosts);
54+ },
55+ shouldTrack(pathname) {
56+ const lower = pathname.toLowerCase();
57+ return pathname.includes("/backend-api/")
58+ || pathname.includes("/backend-anon/")
59+ || pathname.includes("/public-api/")
60+ || lower.includes("/conversation")
61+ || lower.includes("/models")
62+ || lower.includes("/files")
63+ || (!isLikelyStaticPath(pathname) && (lower.includes("/api/") || lower.includes("/backend")));
64+ },
65+ isSse(pathname, contentType) {
66+ const lower = pathname.toLowerCase();
67+ return contentType.includes("text/event-stream")
68+ || lower.includes("/conversation")
69+ || lower.includes("/backend-api/conversation");
70+ }
71+ },
72+ {
73+ platform: "gemini",
74+ pageHosts: ["gemini.google.com"],
75+ requestHosts: ["gemini.google.com"],
76+ matchesPageHost(hostname) {
77+ return hostnameMatches(hostname, this.pageHosts);
78+ },
79+ matchesRequestHost(hostname) {
80+ return hostnameMatches(hostname, this.requestHosts);
81+ },
82+ shouldTrack(pathname) {
83+ const lower = pathname.toLowerCase();
84+ return pathname.startsWith("/_/")
85+ || pathname.includes("/api/")
86+ || lower.includes("bardchatui")
87+ || lower.includes("streamgenerate")
88+ || lower.includes("generatecontent")
89+ || lower.includes("modelresponse")
90+ || (!isLikelyStaticPath(pathname) && lower.includes("assistant"));
91+ },
92+ isSse(pathname, contentType) {
93+ return contentType.includes("text/event-stream") || pathname.toLowerCase().includes("streamgenerate");
94+ }
95+ }
96+ ];
97+
98+ function findPageRule(hostname) {
99+ return PLATFORM_RULES.find((rule) => rule.matchesPageHost(hostname)) || null;
100+ }
101+
102+ function findRequestRule(hostname) {
103+ return PLATFORM_RULES.find((rule) => rule.matchesRequestHost(hostname)) || null;
104+ }
105+
106+ const pageRule = findPageRule(location.hostname);
107+ if (!pageRule) return;
108+
109+ function getRequestContext(url) {
110+ try {
111+ const parsed = new URL(url, location.href);
112+ const rule = findRequestRule(parsed.hostname);
113+ if (!rule || rule.platform !== pageRule.platform) return null;
114+ return { parsed, rule };
115+ } catch (_) {
116+ return null;
117+ }
118+ }
119+
120+ function shouldTrack(url) {
121+ const context = getRequestContext(url);
122+ return context ? context.rule.shouldTrack(context.parsed.pathname) : false;
123+ }
124+
125+ function readHeaders(headersLike) {
126+ const out = {};
127+ try {
128+ const headers = new Headers(headersLike || {});
129+ headers.forEach((value, key) => {
130+ out[key] = value;
131+ });
132+ } catch (_) {}
133+ return out;
134+ }
135+
136+ function readRawHeaders(rawHeaders) {
137+ const out = {};
138+ for (const line of String(rawHeaders || "").split(/\r?\n/)) {
139+ const index = line.indexOf(":");
140+ if (index <= 0) continue;
141+ const name = line.slice(0, index).trim().toLowerCase();
142+ const value = line.slice(index + 1).trim();
143+ if (name) out[name] = value;
144+ }
145+ return out;
146+ }
147+
148+ function trim(text) {
149+ if (text == null) return null;
150+ return text.length > BODY_LIMIT ? text.slice(0, BODY_LIMIT) : text;
151+ }
152+
153+ function emit(type, detail, rule = pageRule) {
154+ window.dispatchEvent(new CustomEvent(type, {
155+ detail: {
156+ platform: rule.platform,
157+ ...detail
158+ }
159+ }));
160+ }
161+
162+ function emitNet(detail, rule = pageRule) {
163+ emit("__baa_net__", detail, rule);
164+ }
165+
166+ function emitSse(detail, rule = pageRule) {
167+ emit("__baa_sse__", detail, rule);
168+ }
169+
170+ function isForbiddenProxyHeader(name) {
171+ const lower = String(name || "").toLowerCase();
172+ return lower === "accept-encoding"
173+ || lower === "connection"
174+ || lower === "content-length"
175+ || lower === "cookie"
176+ || lower === "host"
177+ || lower === "origin"
178+ || lower === "referer"
179+ || lower === "user-agent"
180+ || lower.startsWith("sec-");
181+ }
182+
183+ emit("__baa_ready__", {
184+ platform: pageRule.platform,
185+ url: location.href,
186+ source: "page-interceptor"
187+ }, pageRule);
188+
189+ function trimBodyValue(body) {
190+ try {
191+ if (body == null) return null;
192+ if (typeof body === "string") return trim(body);
193+ if (body instanceof URLSearchParams) return trim(body.toString());
194+ if (body instanceof FormData) {
195+ const pairs = [];
196+ for (const [key, value] of body.entries()) {
197+ pairs.push([key, typeof value === "string" ? value : `[${value?.constructor?.name || "binary"}]`]);
198+ }
199+ return trim(JSON.stringify(pairs));
200+ }
201+ if (body instanceof Blob) return `[blob ${body.type || "application/octet-stream"} ${body.size}]`;
202+ if (body instanceof ArrayBuffer) return `[arraybuffer ${body.byteLength}]`;
203+ if (ArrayBuffer.isView(body)) return `[typedarray ${body.byteLength}]`;
204+ return trim(JSON.stringify(body));
205+ } catch (_) {
206+ return null;
207+ }
208+ }
209+
210+ async function readRequestBody(input, init) {
211+ try {
212+ if (init && Object.prototype.hasOwnProperty.call(init, "body")) {
213+ return trimBodyValue(init.body);
214+ }
215+ if (input instanceof Request && !input.bodyUsed) {
216+ return trim(await input.clone().text());
217+ }
218+ } catch (_) {}
219+ return null;
220+ }
221+
222+ async function readResponseText(response) {
223+ try {
224+ return trim(await response.clone().text());
225+ } catch (_) {
226+ return null;
227+ }
228+ }
229+
230+ async function streamSse(url, method, requestBody, response, startedAt, rule) {
231+ try {
232+ const clone = response.clone();
233+ if (!clone.body) return;
234+
235+ const reader = clone.body.getReader();
236+ const decoder = new TextDecoder();
237+ let buffer = "";
238+
239+ while (true) {
240+ const { done, value } = await reader.read();
241+ if (done) break;
242+ buffer += decoder.decode(value, { stream: true });
243+
244+ const chunks = buffer.split("\n\n");
245+ buffer = chunks.pop() || "";
246+
247+ for (const chunk of chunks) {
248+ if (!chunk.trim()) continue;
249+ emitSse({
250+ url,
251+ method,
252+ reqBody: requestBody,
253+ chunk,
254+ ts: Date.now()
255+ }, rule);
256+ }
257+ }
258+
259+ if (buffer.trim()) {
260+ emitSse({
261+ url,
262+ method,
263+ reqBody: requestBody,
264+ chunk: buffer,
265+ ts: Date.now()
266+ }, rule);
267+ }
268+
269+ emitSse({
270+ url,
271+ method,
272+ reqBody: requestBody,
273+ done: true,
274+ ts: Date.now(),
275+ duration: Date.now() - startedAt
276+ }, rule);
277+ } catch (error) {
278+ emitSse({
279+ url,
280+ method,
281+ reqBody: requestBody,
282+ error: error.message,
283+ ts: Date.now()
284+ }, rule);
285+ }
286+ }
287+
288+ window.addEventListener("__baa_proxy_request__", async (event) => {
289+ let detail = event.detail || {};
290+ if (typeof detail === "string") {
291+ try {
292+ detail = JSON.parse(detail);
293+ } catch (_) {
294+ detail = {};
295+ }
296+ }
297+
298+ const id = detail.id;
299+ const method = String(detail.method || "GET").toUpperCase();
300+ const rawPath = detail.path || detail.url || location.href;
301+
302+ if (!id) return;
303+
304+ try {
305+ const url = new URL(rawPath, location.origin).href;
306+ const context = getRequestContext(url);
307+ const headers = new Headers();
308+ const startedAt = Date.now();
309+
310+ for (const [name, value] of Object.entries(detail.headers || {})) {
311+ if (!name || value == null || value === "") continue;
312+ if (isForbiddenProxyHeader(name)) continue;
313+ headers.set(String(name).toLowerCase(), String(value));
314+ }
315+
316+ let body = null;
317+ if (method !== "GET" && method !== "HEAD" && Object.prototype.hasOwnProperty.call(detail, "body")) {
318+ if (typeof detail.body === "string") {
319+ body = detail.body;
320+ } else if (detail.body != null) {
321+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
322+ body = JSON.stringify(detail.body);
323+ }
324+ }
325+
326+ const response = await originalFetch.call(window, url, {
327+ method,
328+ headers,
329+ body,
330+ credentials: "include"
331+ });
332+
333+ const responseBody = await response.text();
334+ const resHeaders = readHeaders(response.headers);
335+ const contentType = response.headers.get("content-type") || "";
336+ const isSse = context ? context.rule.isSse(context.parsed.pathname, contentType) : false;
337+ const reqHeaders = readHeaders(headers);
338+ const reqBody = typeof body === "string" ? trim(body) : null;
339+ const trimmedResponseBody = trim(responseBody);
340+
341+ emitNet({
342+ url,
343+ method,
344+ reqHeaders,
345+ reqBody,
346+ status: response.status,
347+ resHeaders,
348+ resBody: isSse && pageRule.platform !== "gemini" ? null : trimmedResponseBody,
349+ duration: Date.now() - startedAt,
350+ sse: isSse,
351+ source: "proxy"
352+ }, pageRule);
353+
354+ if (isSse && trimmedResponseBody) {
355+ emitSse({
356+ url,
357+ method,
358+ reqBody,
359+ chunk: trimmedResponseBody,
360+ ts: Date.now()
361+ }, pageRule);
362+ emitSse({
363+ url,
364+ method,
365+ reqBody,
366+ done: true,
367+ ts: Date.now(),
368+ duration: Date.now() - startedAt
369+ }, pageRule);
370+ }
371+
372+ emit("__baa_proxy_response__", {
373+ id,
374+ platform: pageRule.platform,
375+ url,
376+ method,
377+ ok: response.ok,
378+ status: response.status,
379+ body: responseBody
380+ }, pageRule);
381+ } catch (error) {
382+ emitNet({
383+ url: rawPath,
384+ method,
385+ reqHeaders: readHeaders(detail.headers || {}),
386+ reqBody: typeof detail.body === "string" ? trim(detail.body) : trimBodyValue(detail.body),
387+ error: error.message,
388+ source: "proxy"
389+ }, pageRule);
390+ emit("__baa_proxy_response__", {
391+ id,
392+ platform: pageRule.platform,
393+ url: rawPath,
394+ method,
395+ ok: false,
396+ error: error.message
397+ }, pageRule);
398+ }
399+ });
400+
401+ window.fetch = async function patchedFetch(input, init) {
402+ const url = input instanceof Request ? input.url : String(input);
403+ const context = getRequestContext(url);
404+ if (!context || !context.rule.shouldTrack(context.parsed.pathname)) {
405+ return originalFetch.apply(this, arguments);
406+ }
407+
408+ const method = ((init && init.method) || (input instanceof Request ? input.method : "GET")).toUpperCase();
409+ const startedAt = Date.now();
410+ const reqHeaders = readHeaders(init && init.headers ? init.headers : (input instanceof Request ? input.headers : null));
411+ const reqBody = await readRequestBody(input, init);
412+
413+ try {
414+ const response = await originalFetch.apply(this, arguments);
415+ const resHeaders = readHeaders(response.headers);
416+ const contentType = response.headers.get("content-type") || "";
417+ const isSse = context.rule.isSse(context.parsed.pathname, contentType);
418+
419+ if (isSse) {
420+ emitNet({
421+ url,
422+ method,
423+ reqHeaders,
424+ reqBody,
425+ status: response.status,
426+ resHeaders,
427+ duration: Date.now() - startedAt,
428+ sse: true,
429+ source: "page"
430+ }, context.rule);
431+ streamSse(url, method, reqBody, response, startedAt, context.rule);
432+ return response;
433+ }
434+
435+ const resBody = await readResponseText(response);
436+ emitNet({
437+ url,
438+ method,
439+ reqHeaders,
440+ reqBody,
441+ status: response.status,
442+ resHeaders,
443+ resBody,
444+ duration: Date.now() - startedAt,
445+ source: "page"
446+ }, context.rule);
447+ return response;
448+ } catch (error) {
449+ emitNet({
450+ url,
451+ method,
452+ reqHeaders,
453+ reqBody,
454+ error: error.message,
455+ duration: Date.now() - startedAt,
456+ source: "page"
457+ }, context.rule);
458+ throw error;
459+ }
460+ };
461+
462+ XMLHttpRequest.prototype.open = function patchedOpen(method, url) {
463+ this.__baaMethod = String(method || "GET").toUpperCase();
464+ this.__baaUrl = typeof url === "string" ? url : String(url);
465+ this.__baaRequestHeaders = {};
466+ return originalXhrOpen.apply(this, arguments);
467+ };
468+
469+ XMLHttpRequest.prototype.setRequestHeader = function patchedSetRequestHeader(name, value) {
470+ if (this.__baaRequestHeaders && name) {
471+ this.__baaRequestHeaders[String(name).toLowerCase()] = String(value || "");
472+ }
473+ return originalXhrSetRequestHeader.apply(this, arguments);
474+ };
475+
476+ XMLHttpRequest.prototype.send = function patchedSend(body) {
477+ const url = this.__baaUrl;
478+ const context = getRequestContext(url);
479+ if (!context || !context.rule.shouldTrack(context.parsed.pathname)) {
480+ return originalXhrSend.apply(this, arguments);
481+ }
482+
483+ const method = this.__baaMethod || "GET";
484+ const reqBody = trimBodyValue(body);
485+ const reqHeaders = { ...(this.__baaRequestHeaders || {}) };
486+ const startedAt = Date.now();
487+ let finalized = false;
488+ let terminalError = null;
489+
490+ const finalize = () => {
491+ if (finalized) return;
492+ finalized = true;
493+
494+ const resHeaders = readRawHeaders(this.getAllResponseHeaders());
495+ const contentType = String(this.getResponseHeader("content-type") || "");
496+ const duration = Date.now() - startedAt;
497+ const error = terminalError || (this.status === 0 ? "xhr_failed" : null);
498+ const isSse = context.rule.isSse(context.parsed.pathname, contentType);
499+ let responseText = null;
500+
501+ try {
502+ responseText = typeof this.responseText === "string" ? trim(this.responseText) : null;
503+ } catch (_) {
504+ responseText = null;
505+ }
506+
507+ emitNet({
508+ url,
509+ method,
510+ reqHeaders,
511+ reqBody,
512+ status: this.status || null,
513+ resHeaders,
514+ resBody: isSse && context.rule.platform !== "gemini" ? null : responseText,
515+ error,
516+ duration,
517+ sse: isSse,
518+ source: "xhr"
519+ }, context.rule);
520+
521+ if (isSse && responseText) {
522+ emitSse({
523+ url,
524+ method,
525+ reqBody,
526+ chunk: responseText,
527+ ts: Date.now()
528+ }, context.rule);
529+ }
530+
531+ if (isSse) {
532+ emitSse({
533+ url,
534+ method,
535+ reqBody,
536+ done: true,
537+ ts: Date.now(),
538+ duration
539+ }, context.rule);
540+ }
541+ };
542+
543+ this.addEventListener("error", () => {
544+ terminalError = "xhr_error";
545+ }, { once: true });
546+ this.addEventListener("abort", () => {
547+ terminalError = "xhr_aborted";
548+ }, { once: true });
549+ this.addEventListener("timeout", () => {
550+ terminalError = "xhr_timeout";
551+ }, { once: true });
552+ this.addEventListener("loadend", finalize, { once: true });
553+
554+ return originalXhrSend.apply(this, arguments);
555+ };
556+})();
1@@ -0,0 +1,29 @@
2+#!/usr/bin/env bash
3+set -euo pipefail
4+
5+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
7+
8+WEB_EXT_BIN="${BAA_WEB_EXT_BIN:-web-ext}"
9+FIREFOX_BIN="${BAA_FIREFOX_BIN:-/Applications/Firefox.app/Contents/MacOS/firefox}"
10+PROFILE_NAME="${BAA_FIREFOX_PROFILE:-baa-firefox-persistent}"
11+
12+if ! command -v "${WEB_EXT_BIN}" >/dev/null 2>&1; then
13+ echo "web-ext not found: ${WEB_EXT_BIN}" >&2
14+ exit 1
15+fi
16+
17+if [ ! -x "${FIREFOX_BIN}" ]; then
18+ echo "Firefox binary not found: ${FIREFOX_BIN}" >&2
19+ exit 1
20+fi
21+
22+exec "${WEB_EXT_BIN}" run \
23+ --source-dir "${REPO_DIR}" \
24+ --firefox "${FIREFOX_BIN}" \
25+ --target firefox-desktop \
26+ --firefox-profile "${PROFILE_NAME}" \
27+ --keep-profile-changes \
28+ --pref=network.proxy.allow_hijacking_localhost=false \
29+ --pref=network.proxy.no_proxies_on=localhost,127.0.0.1 \
30+ "$@"