diff --git a/apps/x/apps/main/src/main.ts b/apps/x/apps/main/src/main.ts index 3b8b70c8..d828f38d 100644 --- a/apps/x/apps/main/src/main.ts +++ b/apps/x/apps/main/src/main.ts @@ -25,6 +25,7 @@ import { init as initAgentNotes } from "@x/core/dist/knowledge/agent_notes.js"; import { initConfigs } from "@x/core/dist/config/initConfigs.js"; import started from "electron-squirrel-startup"; import { execSync } from "node:child_process"; +import { init as initChromeSync } from "@x/core/dist/knowledge/chrome-extension/server/server.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -234,6 +235,9 @@ app.whenReady().then(async () => { // start agent notes learning service initAgentNotes(); + // start chrome extension sync server + initChromeSync(); + app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); diff --git a/apps/x/packages/core/package.json b/apps/x/packages/core/package.json index 53495637..72d6f079 100644 --- a/apps/x/packages/core/package.json +++ b/apps/x/packages/core/package.json @@ -24,7 +24,9 @@ "ai": "^5.0.133", "awilix": "^12.0.5", "chokidar": "^4.0.3", + "cors": "^2.8.6", "cron-parser": "^5.5.0", + "express": "^5.2.1", "glob": "^13.0.0", "google-auth-library": "^10.5.0", "isomorphic-git": "^1.29.0", @@ -41,6 +43,8 @@ "zod": "^4.2.1" }, "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.6", "@types/node": "^25.0.3", "@types/papaparse": "^5.5.2", "@types/pdf-parse": "^1.1.5" diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/README.md b/apps/x/packages/core/src/knowledge/chrome-extension/README.md new file mode 100644 index 00000000..b7de8636 --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/README.md @@ -0,0 +1,96 @@ +# Page Capture Chrome Extension + +A Chrome extension that captures web pages you visit and sends them to a local server for storage as markdown files. + +## Structure + +``` +/extension + manifest.json # Chrome extension manifest (v3) + background.js # Service worker that captures pages +/server + server.py # Flask server for storing captures + captured_pages/ # Directory where pages are saved +``` + +## Setup + +### 1. Install Server Dependencies + +```bash +cd server +pip install flask flask-cors +``` + +### 2. Start the Server + +```bash +cd server +python server.py +``` + +The server will run at `http://localhost:3001`. + +### 3. Install the Chrome Extension + +1. Open Chrome and navigate to `chrome://extensions/` +2. Enable "Developer mode" (toggle in top right) +3. Click "Load unpacked" +4. Select the `extension` folder + +## Usage + +Once both the server is running and the extension is installed, the extension will automatically capture pages as you browse: + +- Every page load (http/https URLs only) triggers a capture +- Content is hashed with SHA-256 to avoid duplicate captures +- Pages are saved as markdown files with frontmatter metadata + +## API Endpoints + +### POST /capture + +Receives captured page data. + +**Request body:** +```json +{ + "url": "https://example.com", + "content": "Page text content...", + "timestamp": 1706123456789, + "title": "Page Title" +} +``` + +**Response:** +```json +{"status": "captured", "filename": "1706123456789_example_com.md"} +``` + +### GET /status + +Returns the count of captured pages. + +**Response:** +```json +{"count": 42} +``` + +## File Format + +Captured pages are saved as markdown with YAML frontmatter: + +```markdown +--- +url: https://example.com/page +title: Page Title +captured_at: 2024-01-24T12:34:56 +--- + +Page content here... +``` + +## Debugging + +- **Extension logs**: Open `chrome://extensions/`, find "Page Capture", click "Service worker" to view console logs +- **Server logs**: Check the terminal where `server.py` is running diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/background.js b/apps/x/packages/core/src/knowledge/chrome-extension/extension/background.js new file mode 100644 index 00000000..235a292b --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/extension/background.js @@ -0,0 +1,388 @@ +const SERVER_URL = 'http://localhost:3001'; +const contentHashMap = new Map(); + +let cachedConfig = null; +let serverReachable = true; + +// Default config +const DEFAULT_CONFIG = { + mode: 'ask', + whitelist: [], + blacklist: [], + enabled: true +}; + +// Config management +async function loadConfig() { + try { + const response = await fetch(`${SERVER_URL}/browse/config`); + if (response.ok) { + cachedConfig = await response.json(); + serverReachable = true; + } else { + throw new Error('Server returned error'); + } + } catch (error) { + console.log(`[Page Capture] Failed to load config: ${error.message}`); + serverReachable = false; + cachedConfig = cachedConfig || DEFAULT_CONFIG; + } + return cachedConfig; +} + +async function saveConfig(config) { + try { + const response = await fetch(`${SERVER_URL}/browse/config`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(config) + }); + if (response.ok) { + cachedConfig = config; + serverReachable = true; + return true; + } + } catch (error) { + console.log(`[Page Capture] Failed to save config: ${error.message}`); + serverReachable = false; + } + return false; +} + +function getConfig() { + return cachedConfig || DEFAULT_CONFIG; +} + +function extractDomain(url) { + try { + const parsed = new URL(url); + return parsed.hostname; + } catch { + return null; + } +} + +function isWhitelisted(domain) { + const config = getConfig(); + return config.whitelist.some(d => domain === d || domain.endsWith('.' + d)); +} + +function isBlacklisted(domain) { + const config = getConfig(); + return config.blacklist.some(d => domain === d || domain.endsWith('.' + d)); +} + +function getDomainStatus(domain) { + const config = getConfig(); + if (isBlacklisted(domain)) return 'blacklisted'; + if (config.mode === 'all') return 'capturing'; + if (isWhitelisted(domain)) return 'whitelisted'; + return 'unknown'; +} + +function shouldCapture(domain) { + const config = getConfig(); + if (!config.enabled) return false; + if (isBlacklisted(domain)) return false; + if (config.mode === 'all') return true; + return isWhitelisted(domain); +} + +// Badge management +async function setBadge(tabId, type) { + try { + if (type === 'needs-approval') { + await chrome.action.setBadgeText({ tabId, text: '?' }); + await chrome.action.setBadgeBackgroundColor({ tabId, color: '#F59E0B' }); + } else if (type === 'server-error') { + await chrome.action.setBadgeText({ tabId, text: '!' }); + await chrome.action.setBadgeBackgroundColor({ tabId, color: '#EF4444' }); + } else { + await chrome.action.setBadgeText({ tabId, text: '' }); + } + } catch (error) { + console.log(`[Page Capture] Failed to set badge: ${error.message}`); + } +} + +async function updateBadgeForTab(tabId, url) { + if (!serverReachable) { + await setBadge(tabId, 'server-error'); + return; + } + + const domain = extractDomain(url); + if (!domain) { + await setBadge(tabId, 'clear'); + return; + } + + const status = getDomainStatus(domain); + if (status === 'unknown') { + await setBadge(tabId, 'needs-approval'); + } else { + await setBadge(tabId, 'clear'); + } +} + +// Content hashing +async function hashContent(content) { + const encoder = new TextEncoder(); + const data = encoder.encode(content); + const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); +} + +function isValidUrl(url) { + if (!url) return false; + try { + const parsed = new URL(url); + return parsed.protocol === 'http:' || parsed.protocol === 'https:'; + } catch { + return false; + } +} + +async function capturePageContent(tabId) { + try { + const results = await chrome.scripting.executeScript({ + target: { tabId }, + func: () => document.body.innerText + }); + return results[0]?.result || ''; + } catch (error) { + console.log(`[Page Capture] Failed to capture content: ${error.message}`); + return null; + } +} + +async function sendToServer(data) { + try { + const response = await fetch(`${SERVER_URL}/capture`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + serverReachable = response.ok; + return response.ok; + } catch (error) { + console.log(`[Page Capture] Failed to send to server: ${error.message}`); + serverReachable = false; + return false; + } +} + +async function captureTab(tabId, tab) { + const content = await capturePageContent(tabId); + if (content === null) return false; + + const hash = await hashContent(content); + const lastHash = contentHashMap.get(tab.url); + + if (lastHash === hash) { + console.log(`[Page Capture] Content unchanged for: ${tab.url}`); + return true; + } + + contentHashMap.set(tab.url, hash); + + const payload = { + url: tab.url, + content, + timestamp: Date.now(), + title: tab.title || 'Untitled' + }; + + const success = await sendToServer(payload); + if (success) { + console.log(`[Page Capture] Captured: ${tab.url}`); + } + return success; +} + +// Tab update listener +chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { + if (changeInfo.status !== 'complete') return; + if (!isValidUrl(tab.url)) { + console.log(`[Page Capture] Skipping non-http URL: ${tab.url}`); + return; + } + + const domain = extractDomain(tab.url); + if (!domain) return; + + await updateBadgeForTab(tabId, tab.url); + + if (!shouldCapture(domain)) { + console.log(`[Page Capture] Skipping (not whitelisted): ${tab.url}`); + return; + } + + await captureTab(tabId, tab); +}); + +// Tab activated listener - update badge +chrome.tabs.onActivated.addListener(async (activeInfo) => { + try { + const tab = await chrome.tabs.get(activeInfo.tabId); + if (tab.url && isValidUrl(tab.url)) { + await updateBadgeForTab(activeInfo.tabId, tab.url); + } + } catch (error) { + console.log(`[Page Capture] Failed to update badge on tab switch: ${error.message}`); + } +}); + +// Handle scroll capture messages from content script +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.type === 'SCROLL_CAPTURE') { + const { url, content, timestamp, title, scrollY } = message; + const domain = extractDomain(url); + + if (!shouldCapture(domain)) { + console.log(`[Page Capture] Skipping scroll capture (not whitelisted): ${url}`); + return; + } + + console.log(`[Page Capture] Received scroll capture for: ${url}`); + + hashContent(content).then(async (hash) => { + const lastHash = contentHashMap.get(url); + if (lastHash === hash) { + console.log(`[Page Capture] Hash unchanged, skipping: ${url}`); + return; + } + + contentHashMap.set(url, hash); + + const payload = { url, content, timestamp, title }; + const success = await sendToServer(payload); + if (success) { + console.log(`[Page Capture] Scroll captured (y=${scrollY}): ${url}`); + } + }); + return; + } + + // Handle messages from popup + if (message.type === 'GET_CONFIG') { + loadConfig().then(config => { + sendResponse({ config, serverReachable }); + }); + return true; + } + + if (message.type === 'SAVE_CONFIG') { + saveConfig(message.config).then(success => { + sendResponse({ success }); + // Update badges on all tabs + chrome.tabs.query({}, tabs => { + tabs.forEach(tab => { + if (tab.url && isValidUrl(tab.url)) { + updateBadgeForTab(tab.id, tab.url); + } + }); + }); + }); + return true; + } + + if (message.type === 'GET_DOMAIN_STATUS') { + const domain = extractDomain(message.url); + const status = domain ? getDomainStatus(domain) : 'unknown'; + sendResponse({ status, domain, serverReachable }); + return true; + } + + if (message.type === 'APPROVE_DOMAIN') { + const config = getConfig(); + const domain = message.domain; + if (!config.whitelist.includes(domain)) { + config.whitelist.push(domain); + } + config.blacklist = config.blacklist.filter(d => d !== domain); + saveConfig(config).then(success => { + sendResponse({ success }); + chrome.tabs.query({}, tabs => { + tabs.forEach(tab => { + if (tab.url && isValidUrl(tab.url)) { + updateBadgeForTab(tab.id, tab.url); + } + }); + }); + }); + return true; + } + + if (message.type === 'REJECT_DOMAIN') { + const config = getConfig(); + const domain = message.domain; + if (!config.blacklist.includes(domain)) { + config.blacklist.push(domain); + } + config.whitelist = config.whitelist.filter(d => d !== domain); + saveConfig(config).then(success => { + sendResponse({ success }); + chrome.tabs.query({}, tabs => { + tabs.forEach(tab => { + if (tab.url && isValidUrl(tab.url)) { + updateBadgeForTab(tab.id, tab.url); + } + }); + }); + }); + return true; + } + + if (message.type === 'CAPTURE_ONCE') { + chrome.tabs.query({ active: true, currentWindow: true }, async tabs => { + if (tabs[0]) { + const success = await captureTab(tabs[0].id, tabs[0]); + sendResponse({ success }); + } else { + sendResponse({ success: false }); + } + }); + return true; + } + + if (message.type === 'REMOVE_FROM_WHITELIST') { + const config = getConfig(); + config.whitelist = config.whitelist.filter(d => d !== message.domain); + saveConfig(config).then(success => { + sendResponse({ success }); + chrome.tabs.query({}, tabs => { + tabs.forEach(tab => { + if (tab.url && isValidUrl(tab.url)) { + updateBadgeForTab(tab.id, tab.url); + } + }); + }); + }); + return true; + } + + if (message.type === 'REMOVE_FROM_BLACKLIST') { + const config = getConfig(); + config.blacklist = config.blacklist.filter(d => d !== message.domain); + saveConfig(config).then(success => { + sendResponse({ success }); + chrome.tabs.query({}, tabs => { + tabs.forEach(tab => { + if (tab.url && isValidUrl(tab.url)) { + updateBadgeForTab(tab.id, tab.url); + } + }); + }); + }); + return true; + } +}); + +// Load config on startup +loadConfig().then(() => { + console.log('[Page Capture] Config loaded'); +}); + +console.log('[Page Capture] Service worker started'); diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/content.js b/apps/x/packages/core/src/knowledge/chrome-extension/extension/content.js new file mode 100644 index 00000000..bae24963 --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/extension/content.js @@ -0,0 +1,81 @@ +const DEBOUNCE_MS = 800; +const MIN_SCROLL_PIXELS = 500; +const MIN_CONTENT_CHANGE = 100; // characters + +let debounceTimer = null; +let lastCapturedContent = null; +let lastScrollTop = 0; +let scrollContainer = null; + +function getScrollTop() { + if (!scrollContainer || scrollContainer === window) { + return window.scrollY; + } + if (scrollContainer === document) { + return document.documentElement.scrollTop; + } + return scrollContainer.scrollTop || 0; +} + +function captureAndSend() { + const content = document.body.innerText; + + // Skip if content unchanged or minimal change + if (lastCapturedContent) { + const lengthDiff = Math.abs(content.length - lastCapturedContent.length); + if (content === lastCapturedContent || lengthDiff < MIN_CONTENT_CHANGE) { + return; + } + } + + lastCapturedContent = content; + lastScrollTop = getScrollTop(); + + chrome.runtime.sendMessage({ + type: 'SCROLL_CAPTURE', + url: window.location.href, + title: document.title, + content: content, + timestamp: Date.now(), + scrollY: lastScrollTop + }); +} + +function onScroll() { + const currentScrollTop = getScrollTop(); + const scrollDelta = Math.abs(currentScrollTop - lastScrollTop); + + if (scrollDelta < MIN_SCROLL_PIXELS) { + return; + } + + if (debounceTimer) { + clearTimeout(debounceTimer); + } + + debounceTimer = setTimeout(() => { + captureAndSend(); + }, DEBOUNCE_MS); +} + +function init() { + // Use document with capture to catch scroll events from any element + document.addEventListener('scroll', (e) => { + const target = e.target; + const scrollTop = target === document ? document.documentElement.scrollTop : target.scrollTop; + + // Update scroll container if we found the real one + if (scrollTop > 0 && scrollContainer !== target) { + scrollContainer = target; + } + + onScroll(); + }, { capture: true, passive: true }); +} + +// Wait for page to be ready, then init +if (document.readyState === 'complete') { + init(); +} else { + window.addEventListener('load', init); +} diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/icon.png b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icon.png new file mode 100644 index 00000000..e2fd6386 Binary files /dev/null and b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icon.png differ diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon128.png b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon128.png new file mode 100644 index 00000000..c8cc16e3 Binary files /dev/null and b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon128.png differ diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon16.png b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon16.png new file mode 100644 index 00000000..482631a1 Binary files /dev/null and b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon16.png differ diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon32.png b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon32.png new file mode 100644 index 00000000..2b95aa62 Binary files /dev/null and b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon32.png differ diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon48.png b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon48.png new file mode 100644 index 00000000..f20bf478 Binary files /dev/null and b/apps/x/packages/core/src/knowledge/chrome-extension/extension/icons/icon48.png differ diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/manifest.json b/apps/x/packages/core/src/knowledge/chrome-extension/extension/manifest.json new file mode 100644 index 00000000..f536adec --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/extension/manifest.json @@ -0,0 +1,40 @@ +{ + "manifest_version": 3, + "name": "Rowboat Browser Capture", + "version": "1.1.1", + "description": "Allows users to save and capture web page content to their Rowboat workspace.", + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + }, + "permissions": [ + "tabs", + "scripting", + "activeTab" + ], + "host_permissions": [ + "http://*/*", + "https://*/*" + ], + "action": { + "default_popup": "popup.html", + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } + }, + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*"], + "js": ["content.js"], + "run_at": "document_idle" + } + ] +} diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/popup.html b/apps/x/packages/core/src/knowledge/chrome-extension/extension/popup.html new file mode 100644 index 00000000..b8d55d71 --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/extension/popup.html @@ -0,0 +1,174 @@ + + + + + + Rowboat + + + + +
+ - + + + - + +
+ + + + + + + +
+
Settings
+
+ + +
+
+ +
+ - +
+ + + + diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/popup.js b/apps/x/packages/core/src/knowledge/chrome-extension/extension/popup.js new file mode 100644 index 00000000..6a3fc0b1 --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/extension/popup.js @@ -0,0 +1,258 @@ +const SERVER_URL = 'http://localhost:3001'; + + +let currentDomain = null; +let currentStatus = null; +let currentConfig = null; + +async function getCurrentTab() { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + return tab; +} + +function extractDomain(url) { + try { + const parsed = new URL(url); + return parsed.hostname; + } catch { + return null; + } +} + +function updateStatusBadge(status, serverReachable) { + const badge = document.getElementById('statusBadge'); + const statusText = document.getElementById('statusText'); + + badge.classList.remove('capturing', 'not-capturing', 'awaiting', 'error'); + + if (!serverReachable) { + badge.classList.add('error'); + statusText.textContent = 'Error'; + return; + } + + switch (status) { + case 'whitelisted': + case 'capturing': + badge.classList.add('capturing'); + statusText.textContent = 'Indexing'; + break; + case 'blacklisted': + badge.classList.add('not-capturing'); + statusText.textContent = 'Not indexing'; + break; + case 'unknown': + badge.classList.add('awaiting'); + statusText.textContent = 'Awaiting'; + break; + default: + badge.classList.add('not-capturing'); + statusText.textContent = 'Unknown'; + } +} + +function showApprovalSection(show) { + document.getElementById('approvalSection').classList.toggle('hidden', !show); +} + +function showToggleSection(show, isCapturing) { + const section = document.getElementById('toggleSection'); + const label = document.getElementById('toggleLabel'); + const btn = document.getElementById('toggleBtn'); + + section.classList.toggle('hidden', !show); + + if (isCapturing) { + label.textContent = 'Capturing this site'; + btn.textContent = 'Stop'; + btn.onclick = () => removeDomain('whitelist'); + } else { + label.textContent = 'Not capturing this site'; + btn.textContent = 'Start'; + btn.onclick = () => removeDomain('blacklist'); + } +} + +function showError(show) { + document.getElementById('errorMessage').classList.toggle('hidden', !show); +} + +// Settings section +function getSelectedMode(config) { + return config.mode === 'all' ? 'work' : 'ask'; +} + +function initSettings(config) { + currentConfig = config; + const mode = getSelectedMode(config); + + const radio = document.querySelector(`input[name="captureMode"][value="${mode}"]`); + if (radio) radio.checked = true; +} + +async function saveSettingsFromUI() { + const selectedRadio = document.querySelector('input[name="captureMode"]:checked'); + const mode = selectedRadio ? selectedRadio.value : 'ask'; + + let config; + if (mode === 'work') { + config = { + mode: 'all', + whitelist: currentConfig ? currentConfig.whitelist : [], + blacklist: currentConfig ? currentConfig.blacklist : [], + enabled: true + }; + } else { + config = { + mode: 'ask', + whitelist: currentConfig ? currentConfig.whitelist : [], + blacklist: currentConfig ? currentConfig.blacklist : [], + enabled: true + }; + } + + try { + await chrome.runtime.sendMessage({ type: 'SAVE_CONFIG', config }); + currentConfig = config; + await loadStatus(); + } catch (error) { + console.error('Failed to save settings:', error); + } +} + +// Domain status +async function loadStatus() { + const tab = await getCurrentTab(); + if (!tab || !tab.url) { + document.getElementById('domainDisplay').textContent = 'No page'; + return; + } + + currentDomain = extractDomain(tab.url); + if (!currentDomain) { + document.getElementById('domainDisplay').textContent = 'Invalid URL'; + return; + } + + document.getElementById('domainDisplay').textContent = currentDomain; + + try { + const response = await chrome.runtime.sendMessage({ + type: 'GET_DOMAIN_STATUS', + url: tab.url + }); + + currentStatus = response.status; + const serverReachable = response.serverReachable; + + updateStatusBadge(currentStatus, serverReachable); + showError(!serverReachable); + + if (!serverReachable) { + showApprovalSection(false); + showToggleSection(false, false); + return; + } + + if (currentStatus === 'unknown') { + showApprovalSection(true); + showToggleSection(false, false); + } else if (currentStatus === 'whitelisted' || currentStatus === 'capturing') { + showApprovalSection(false); + showToggleSection(true, true); + } else if (currentStatus === 'blacklisted') { + showApprovalSection(false); + showToggleSection(true, false); + } else { + showApprovalSection(false); + showToggleSection(false, false); + } + } catch (error) { + console.error('Failed to get status:', error); + showError(true); + } +} + +async function loadStats() { + try { + const response = await fetch(`${SERVER_URL}/status`); + if (response.ok) { + const data = await response.json(); + document.getElementById('statsCount').textContent = `${data.count} pages indexed locally`; + } + } catch (error) { + console.log('Failed to load stats:', error); + } +} + +async function approveDomain() { + if (!currentDomain) return; + try { + await chrome.runtime.sendMessage({ type: 'APPROVE_DOMAIN', domain: currentDomain }); + // Reload config to reflect the new whitelist in settings + const resp = await chrome.runtime.sendMessage({ type: 'GET_CONFIG' }); + if (resp && resp.config) initSettings(resp.config); + await loadStatus(); + } catch (error) { + console.error('Failed to approve domain:', error); + } +} + +async function rejectDomain() { + if (!currentDomain) return; + try { + await chrome.runtime.sendMessage({ type: 'REJECT_DOMAIN', domain: currentDomain }); + await loadStatus(); + } catch (error) { + console.error('Failed to reject domain:', error); + } +} + +async function captureOnce() { + try { + const response = await chrome.runtime.sendMessage({ type: 'CAPTURE_ONCE' }); + if (response.success) { + window.close(); + } + } catch (error) { + console.error('Failed to capture:', error); + } +} + +async function removeDomain(list) { + if (!currentDomain) return; + try { + const messageType = list === 'whitelist' ? 'REMOVE_FROM_WHITELIST' : 'REMOVE_FROM_BLACKLIST'; + await chrome.runtime.sendMessage({ type: messageType, domain: currentDomain }); + // Reload config to reflect changes in settings + const resp = await chrome.runtime.sendMessage({ type: 'GET_CONFIG' }); + if (resp && resp.config) initSettings(resp.config); + await loadStatus(); + } catch (error) { + console.error('Failed to remove domain:', error); + } +} + +document.addEventListener('DOMContentLoaded', async () => { + // Load config and init settings + try { + const resp = await chrome.runtime.sendMessage({ type: 'GET_CONFIG' }); + if (resp && resp.config) { + initSettings(resp.config); + } + } catch (error) { + console.error('Failed to load config:', error); + } + + // Radio change listeners + document.querySelectorAll('input[name="captureMode"]').forEach(radio => { + radio.addEventListener('change', () => saveSettingsFromUI()); + }); + + loadStatus(); + loadStats(); + + document.getElementById('approveBtn').addEventListener('click', approveDomain); + document.getElementById('rejectBtn').addEventListener('click', rejectDomain); + document.getElementById('captureOnceBtn').addEventListener('click', captureOnce); +}); diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/extension/styles.css b/apps/x/packages/core/src/knowledge/chrome-extension/extension/styles.css new file mode 100644 index 00000000..399c473b --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/extension/styles.css @@ -0,0 +1,279 @@ +:root { + --bg-primary: #ffffff; + --bg-secondary: #f9fafb; + --bg-tertiary: #f3f4f6; + --text-primary: #111827; + --text-secondary: #6b7280; + --text-muted: #9ca3af; + --border-color: #e5e7eb; + --accent-color: #3b82f6; + --accent-hover: #2563eb; + --success-color: #10b981; + --warning-color: #f59e0b; + --error-color: #ef4444; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +@media (prefers-color-scheme: dark) { + :root { + --bg-primary: #1f2937; + --bg-secondary: #111827; + --bg-tertiary: #374151; + --text-primary: #f9fafb; + --text-secondary: #d1d5db; + --text-muted: #9ca3af; + --border-color: #374151; + --accent-color: #60a5fa; + --accent-hover: #3b82f6; + --success-color: #34d399; + --warning-color: #fbbf24; + --error-color: #f87171; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.3); + } +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + line-height: 1.5; + color: var(--text-primary); + background-color: var(--bg-primary); +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 8px 16px; + font-size: 14px; + font-weight: 500; + border-radius: 6px; + border: none; + cursor: pointer; + transition: all 0.15s ease; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-primary { + background-color: var(--accent-color); + color: white; +} + +.btn-primary:hover:not(:disabled) { + background-color: var(--accent-hover); +} + +.btn-secondary { + background-color: var(--bg-tertiary); + color: var(--text-primary); + border: 1px solid var(--border-color); +} + +.btn-secondary:hover:not(:disabled) { + background-color: var(--border-color); +} + +.btn-ghost { + background-color: transparent; + color: var(--text-secondary); +} + +.btn-ghost:hover:not(:disabled) { + background-color: var(--bg-tertiary); + color: var(--text-primary); +} + +.btn-sm { + padding: 4px 8px; + font-size: 12px; +} + +.btn-block { + width: 100%; +} + +/* Status badges */ +.status-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 9999px; + font-size: 12px; + font-weight: 500; +} + +.status-badge.capturing { + background-color: rgba(16, 185, 129, 0.1); + color: var(--success-color); +} + +.status-badge.not-capturing { + background-color: rgba(107, 114, 128, 0.1); + color: var(--text-secondary); +} + +.status-badge.awaiting { + background-color: rgba(245, 158, 11, 0.1); + color: var(--warning-color); +} + +.status-badge.error { + background-color: rgba(239, 68, 68, 0.1); + color: var(--error-color); +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: currentColor; +} + +/* Cards */ +.card { + background-color: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 16px; +} + +/* Form elements */ +.radio-group { + display: flex; + flex-direction: column; + gap: 12px; +} + +.radio-option { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px; + border: 1px solid var(--border-color); + border-radius: 8px; + cursor: pointer; + transition: all 0.15s ease; +} + +.radio-option:hover { + border-color: var(--accent-color); + background-color: var(--bg-secondary); +} + +.radio-option.selected { + border-color: var(--accent-color); + background-color: rgba(59, 130, 246, 0.05); +} + +.radio-option input[type="radio"] { + margin-top: 2px; + accent-color: var(--accent-color); +} + +.radio-option-content { + flex: 1; +} + +.radio-option-title { + font-weight: 500; + color: var(--text-primary); +} + +.radio-option-desc { + font-size: 13px; + color: var(--text-secondary); + margin-top: 2px; +} + +/* Toggle/Checkbox */ +.toggle-list { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 12px; + padding-left: 24px; +} + +.toggle-item { + display: flex; + align-items: center; + gap: 8px; +} + +.toggle-item input[type="checkbox"] { + accent-color: var(--accent-color); +} + +.toggle-item label { + font-size: 13px; + color: var(--text-secondary); + cursor: pointer; +} + +/* Divider */ +.divider { + height: 1px; + background-color: var(--border-color); + margin: 12px 0; +} + +/* Link */ +.link { + color: var(--accent-color); + text-decoration: none; + font-size: 13px; +} + +.link:hover { + text-decoration: underline; +} + +/* Text utilities */ +.text-sm { + font-size: 12px; +} + +.text-muted { + color: var(--text-muted); +} + +.text-secondary { + color: var(--text-secondary); +} + +.text-center { + text-align: center; +} + +/* Spacing utilities */ +.mt-1 { margin-top: 4px; } +.mt-2 { margin-top: 8px; } +.mt-3 { margin-top: 12px; } +.mt-4 { margin-top: 16px; } +.mb-1 { margin-bottom: 4px; } +.mb-2 { margin-bottom: 8px; } +.mb-3 { margin-bottom: 12px; } +.mb-4 { margin-bottom: 16px; } + +/* Flex utilities */ +.flex { display: flex; } +.flex-col { flex-direction: column; } +.items-center { align-items: center; } +.justify-between { justify-content: space-between; } +.gap-1 { gap: 4px; } +.gap-2 { gap: 8px; } +.gap-3 { gap: 12px; } diff --git a/apps/x/packages/core/src/knowledge/chrome-extension/server/server.ts b/apps/x/packages/core/src/knowledge/chrome-extension/server/server.ts new file mode 100644 index 00000000..0cb127b9 --- /dev/null +++ b/apps/x/packages/core/src/knowledge/chrome-extension/server/server.ts @@ -0,0 +1,281 @@ +import express from 'express'; +import cors from 'cors'; +import crypto from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import { WorkDir } from '../../../config/config.js'; + +const app = express(); +app.use(cors()); +app.use(express.json({ limit: '10mb' })); + +const CAPTURED_PAGES_DIR = path.join(WorkDir, 'chrome_sync'); +const CONFIG_DIR = path.join(WorkDir, 'config'); +const CONFIG_FILE = path.join(CONFIG_DIR, 'chrome-plugin.json'); + +interface Config { + mode: 'all' | 'ask'; + whitelist: string[]; + blacklist: string[]; + enabled: boolean; +} + +const DEFAULT_CONFIG: Config = { + mode: 'ask', + whitelist: [], + blacklist: [], + enabled: true +}; + +const contentHashes = new Map(); + +function extractDomain(url: string): string { + try { + const parsed = new URL(url); + return parsed.host || 'unknown'; + } catch { + return 'unknown'; + } +} + +function pathToSlug(url: string): string { + try { + const parsed = new URL(url); + const p = parsed.pathname + (parsed.search || ''); + if (!p || p === '/') return 'index'; + let slug = p.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_|_$/g, ''); + return slug.substring(0, 80) || 'index'; + } catch { + return 'index'; + } +} + +function hashContent(content: string): string { + return crypto.createHash('sha256').update(content, 'utf-8').digest('hex'); +} + +function findExistingFile(domainDir: string, pathSlug: string): string | null { + if (!fs.existsSync(domainDir)) return null; + const files = fs.readdirSync(domainDir); + for (const filename of files) { + if (filename.endsWith(`_${pathSlug}.md`)) { + return path.join(domainDir, filename); + } + } + return null; +} + +// POST /capture +app.post('/capture', (req, res) => { + const data = req.body; + if (!data) { + return res.status(400).json({ error: 'No JSON data provided' }); + } + + const { url, content = '', timestamp, title = 'Untitled' } = data; + + if (!url || !timestamp) { + return res.status(400).json({ error: 'Missing required fields: url, timestamp' }); + } + + const domain = extractDomain(url); + const pathSlug = pathToSlug(url); + const contentHash = hashContent(content); + const cacheKey = `${domain}/${pathSlug}`; + + const dt = new Date(timestamp); + const year = dt.getFullYear(); + const month = String(dt.getMonth() + 1).padStart(2, '0'); + const day = String(dt.getDate()).padStart(2, '0'); + const dateStr = `${year}-${month}-${day}`; + const hours = String(dt.getHours()).padStart(2, '0'); + const minutes = String(dt.getMinutes()).padStart(2, '0'); + const seconds = String(dt.getSeconds()).padStart(2, '0'); + const timeStr = `${hours}-${minutes}`; + const timeDisplay = `${hours}:${minutes}:${seconds}`; + const tzOffset = -dt.getTimezoneOffset(); + const tzSign = tzOffset >= 0 ? '+' : '-'; + const tzHours = String(Math.floor(Math.abs(tzOffset) / 60)).padStart(2, '0'); + const tzMins = String(Math.abs(tzOffset) % 60).padStart(2, '0'); + const isoTimestamp = `${dateStr}T${hours}:${minutes}:${seconds}${tzSign}${tzHours}:${tzMins}`; + + // date/domain directory structure + const domainDir = path.join(CAPTURED_PAGES_DIR, dateStr, domain); + fs.mkdirSync(domainDir, { recursive: true }); + + const existingFile = findExistingFile(domainDir, pathSlug); + if (existingFile && contentHashes.get(cacheKey) === contentHash) { + return res.json({ status: 'skipped', reason: 'duplicate content' }); + } + + contentHashes.set(cacheKey, contentHash); + + // If file exists, append with scroll separator + if (existingFile) { + const scrollSeparator = `\n\n---\nšŸ“œ Scroll captured at ${timeDisplay}\n---\n\n`; + fs.appendFileSync(existingFile, scrollSeparator + content, 'utf-8'); + const rel = `${dateStr}/${domain}/${path.basename(existingFile)}`; + return res.json({ status: 'appended', filename: rel }); + } + + // New file - create with frontmatter + const filename = `${timeStr}_${pathSlug}.md`; + const filepath = path.join(domainDir, filename); + + const markdownContent = `--- +url: ${url} +title: ${title} +captured_at: ${isoTimestamp} +--- + +${content} +`; + + fs.writeFileSync(filepath, markdownContent, 'utf-8'); + return res.status(201).json({ status: 'captured', filename: `${dateStr}/${domain}/${filename}` }); +}); + +// GET /status +app.get('/status', (_req, res) => { + let count = 0; + const domains: Record = {}; + + if (!fs.existsSync(CAPTURED_PAGES_DIR)) { + return res.json({ count: 0, domains: [] }); + } + + for (const dateEntry of fs.readdirSync(CAPTURED_PAGES_DIR)) { + const datePath = path.join(CAPTURED_PAGES_DIR, dateEntry); + if (!fs.statSync(datePath).isDirectory()) continue; + + for (const domainEntry of fs.readdirSync(datePath)) { + const domainPath = path.join(datePath, domainEntry); + if (!fs.statSync(domainPath).isDirectory()) continue; + + const domainCount = fs.readdirSync(domainPath).filter(f => f.endsWith('.md')).length; + count += domainCount; + if (domainCount > 0) { + domains[domainEntry] = (domains[domainEntry] || 0) + domainCount; + } + } + } + + const domainList = Object.entries(domains) + .map(([domain, c]) => ({ domain, count: c })) + .sort((a, b) => b.count - a.count); + + return res.json({ count, domains: domainList }); +}); + +// Config helpers +function loadConfig(): Config { + if (fs.existsSync(CONFIG_FILE)) { + try { + const raw = fs.readFileSync(CONFIG_FILE, 'utf-8'); + return JSON.parse(raw); + } catch { + // fall through + } + } + return { ...DEFAULT_CONFIG }; +} + +function saveConfig(config: Config): void { + fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8'); +} + +function validateConfig(data: any): data is Config { + if (typeof data !== 'object' || data === null) return false; + if (data.mode !== 'all' && data.mode !== 'ask') return false; + if (!Array.isArray(data.whitelist)) return false; + if (!Array.isArray(data.blacklist)) return false; + if (typeof data.enabled !== 'boolean') return false; + return true; +} + +// GET /browse/config +app.get('/browse/config', (_req, res) => { + const config = loadConfig(); + return res.json(config); +}); + +// POST /browse/config +app.post('/browse/config', (req, res) => { + const data = req.body; + if (!data) { + return res.status(400).json({ error: 'No JSON data provided' }); + } + + if (!validateConfig(data)) { + return res.status(400).json({ error: 'Invalid config shape' }); + } + + saveConfig(data); + return res.json({ status: 'saved', config: data }); +}); + +const PORT = 3001; +const RETENTION_DAYS = 7; +const CLEANUP_INTERVAL_MS = 60 * 60 * 1000; // 1 hour + +function cleanUpOldFiles(): void { + if (!fs.existsSync(CAPTURED_PAGES_DIR)) return; + + const cutoff = new Date(); + cutoff.setDate(cutoff.getDate() - RETENTION_DAYS); + const cutoffStr = cutoff.toISOString().slice(0, 10); // YYYY-MM-DD + + for (const dateEntry of fs.readdirSync(CAPTURED_PAGES_DIR)) { + // only process date-formatted directories + if (!/^\d{4}-\d{2}-\d{2}$/.test(dateEntry)) continue; + if (dateEntry >= cutoffStr) continue; + + const datePath = path.join(CAPTURED_PAGES_DIR, dateEntry); + if (!fs.statSync(datePath).isDirectory()) continue; + + fs.rmSync(datePath, { recursive: true, force: true }); + console.log(`[ChromeSync] Cleaned up old captures: ${dateEntry}`); + } +} + +function isServerEnabled(): boolean { + if (!fs.existsSync(CONFIG_FILE)) return false; + try { + const raw = fs.readFileSync(CONFIG_FILE, 'utf-8'); + const config = JSON.parse(raw); + return config.serverEnabled === true; + } catch { + return false; + } +} + +function startServer(): void { + fs.mkdirSync(CAPTURED_PAGES_DIR, { recursive: true }); + + cleanUpOldFiles(); + setInterval(cleanUpOldFiles, CLEANUP_INTERVAL_MS); + + app.listen(PORT, 'localhost', () => { + console.log('[ChromeSync] Server starting.'); + console.log(` Captured pages: ${CAPTURED_PAGES_DIR}`); + console.log(` Config: ${CONFIG_FILE}`); + console.log(` Listening on http://localhost:${PORT}`); + }); +} + +export async function init(): Promise { + fs.mkdirSync(CONFIG_DIR, { recursive: true }); + + if (isServerEnabled()) { + startServer(); + return; + } + + console.log('[ChromeSync] Server disabled, watching config for changes...'); + fs.watch(CONFIG_DIR, (_, filename) => { + if (filename === 'chrome-plugin.json' && isServerEnabled()) { + console.log('[ChromeSync] serverEnabled set to true, starting server...'); + startServer(); + } + }); +} diff --git a/apps/x/pnpm-lock.yaml b/apps/x/pnpm-lock.yaml index c4e701a5..e59ce990 100644 --- a/apps/x/pnpm-lock.yaml +++ b/apps/x/pnpm-lock.yaml @@ -53,9 +53,6 @@ importers: electron-squirrel-startup: specifier: ^1.0.1 version: 1.0.1 - html-to-docx: - specifier: ^1.8.0 - version: 1.8.0(encoding@0.1.13) mammoth: specifier: ^1.11.0 version: 1.11.0 @@ -238,9 +235,6 @@ importers: react-dom: specifier: ^19.2.0 version: 19.2.3(react@19.2.3) - recharts: - specifier: ^3.8.0 - version: 3.8.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@16.13.1)(react@19.2.3)(redux@5.0.1) sonner: specifier: ^2.0.7 version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -310,16 +304,16 @@ importers: dependencies: '@ai-sdk/anthropic': specifier: ^2.0.63 - version: 2.0.63(zod@4.2.1) + version: 2.0.70(zod@4.2.1) '@ai-sdk/google': specifier: ^2.0.53 - version: 2.0.53(zod@4.2.1) + version: 2.0.61(zod@4.2.1) '@ai-sdk/openai': specifier: ^2.0.91 - version: 2.0.91(zod@4.2.1) + version: 2.0.99(zod@4.2.1) '@ai-sdk/openai-compatible': specifier: ^1.0.33 - version: 1.0.33(zod@4.2.1) + version: 1.0.34(zod@4.2.1) '@ai-sdk/provider': specifier: ^2.0.1 version: 2.0.1 @@ -334,7 +328,7 @@ importers: version: 1.25.1(hono@4.11.3)(zod@4.2.1) '@openrouter/ai-sdk-provider': specifier: ^1.2.6 - version: 1.5.4(ai@5.0.133(zod@4.2.1))(zod@4.2.1) + version: 1.5.4(ai@5.0.151(zod@4.2.1))(zod@4.2.1) '@react-pdf/renderer': specifier: ^4.3.2 version: 4.3.2(react@19.2.3) @@ -346,16 +340,22 @@ importers: version: link:../shared ai: specifier: ^5.0.133 - version: 5.0.133(zod@4.2.1) + version: 5.0.151(zod@4.2.1) awilix: specifier: ^12.0.5 version: 12.0.5 chokidar: specifier: ^4.0.3 version: 4.0.3 + cors: + specifier: ^2.8.6 + version: 2.8.6 cron-parser: specifier: ^5.5.0 version: 5.5.0 + express: + specifier: ^5.2.1 + version: 5.2.1 glob: specifier: ^13.0.0 version: 13.0.0 @@ -399,6 +399,12 @@ importers: specifier: ^4.2.1 version: 4.2.1 devDependencies: + '@types/cors': + specifier: ^2.8.19 + version: 2.8.19 + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 '@types/node': specifier: ^25.0.3 version: 25.0.3 @@ -417,8 +423,8 @@ importers: packages: - '@ai-sdk/anthropic@2.0.63': - resolution: {integrity: sha512-zXlUPCkumnvp8lWS9VFcen/MLF6CL/t1zAKDhpobYj9y/nmylQrKtRvn3RwH871Wd3dF3KYEUXd6M2c6dfCKOA==} + '@ai-sdk/anthropic@2.0.70': + resolution: {integrity: sha512-W3WjQlb0Ho+CVAQUvb8Rtk3hGS3Jlgy79ihY2H0yj2k4yU8XuxpQw0Oz+7JQsB47j+jlHhk7nUXtxhAeRg3S3Q==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -429,26 +435,26 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@2.0.39': - resolution: {integrity: sha512-ULnefGmRHG0/tRrf+dtDwgQYAttGi/TR0FmASAzTs1dtpeZp4Xoh1VyWrX3Z1bM3WDs9RM3ZeSE77kQT/jbfjw==} + '@ai-sdk/gateway@2.0.56': + resolution: {integrity: sha512-omvb2Bwpgqg8PKqOpYdIaW+fdEIWcfm2B/j3dx37DxzOIt6fr57VVcfw7pu/EaACcY0O+wsg50iFCPGcsI2Cbg==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/google@2.0.53': - resolution: {integrity: sha512-ccCxr5mrd3AC2CjLq4e1ST7+UiN5T2Pdmgi0XdWM3QohmNBwUQ/RBG7BvL+cB/ex/j6y64tkMmpYz9zBw/SEFQ==} + '@ai-sdk/google@2.0.61': + resolution: {integrity: sha512-hIs7UvL8X5MBG3uxdciSotD4I27UcMa4/we9Qf98fM/RgMTwyk9zXcr7GM6k5yLBZ5S0QeZWkfqKwtdiDnUEEQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai-compatible@1.0.33': - resolution: {integrity: sha512-2KMcR2xAul3u5dGZD7gONgbIki3Hg7Ey+sFu7gsiJ4U2iRU0GDV3ccNq79dTuAEXPDFcOWCUpW8A8jXc0kxJxQ==} + '@ai-sdk/openai-compatible@1.0.34': + resolution: {integrity: sha512-AnGoxVNZ/E3EU4lW12rrufI6riqL2cEv4jk3OrjJ/i54XwR0CJU1V26jXAwxb+Pc+uZmYG++HM+gzXxPQZkMNQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@2.0.91': - resolution: {integrity: sha512-lozfRHfSTHg5/UliQjTDcOtISYGbEpt4FS/6QM5PcLmhdT0HmROllaBmG7+JaK+uqFtDXZGgMIpz3bqB9nzqCQ==} + '@ai-sdk/openai@2.0.99': + resolution: {integrity: sha512-wwa1/DuO9XThaA+sAi0d3+xfkbEx9nRhZ1USV6kktndmEs8aQRR0DJK/Iec+mwNu06IhfDGd5vMscR1U1q155g==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -459,8 +465,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@3.0.21': - resolution: {integrity: sha512-veuMwTLxsgh31Jjn0SnBABnM1f7ebHhRWcV2ZuY3hP3iJDCZ8VXBaYqcHXoOQDqUXTCas08sKQcHyWK+zl882Q==} + '@ai-sdk/provider-utils@3.0.22': + resolution: {integrity: sha512-fFT1KfUUKktfAFm5mClJhS1oux9tP2qgzmEZVl5UdwltQ1LO/s8hd7znVrgKzivwv1s1FIPza0s9OpJaNB/vHw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -1572,46 +1578,6 @@ packages: '@octokit/types@6.41.0': resolution: {integrity: sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==} - '@oozcitak/dom@1.15.5': - resolution: {integrity: sha512-L6v3Mwb0TaYBYgeYlIeBaHnc+2ZEaDSbFiRm5KmqZQSoBlbPlf+l6aIH/sD5GUf2MYwULw00LT7+dOnEuAEC0A==} - engines: {node: '>=8.0'} - - '@oozcitak/dom@1.15.6': - resolution: {integrity: sha512-k4uEIa6DI3FCrFJMGq/05U/59WnS9DjME0kaPqBRCJAqBTkmopbYV1Xs4qFKbDJ/9wOg8W97p+1E0heng/LH7g==} - engines: {node: '>=8.0'} - - '@oozcitak/infra@1.0.3': - resolution: {integrity: sha512-9O2wxXGnRzy76O1XUxESxDGsXT5kzETJPvYbreO4mv6bqe1+YSuux2cZTagjJ/T4UfEwFJz5ixanOqB0QgYAag==} - engines: {node: '>=6.0'} - - '@oozcitak/infra@1.0.5': - resolution: {integrity: sha512-o+zZH7M6l5e3FaAWy3ojaPIVN5eusaYPrKm6MZQt0DKNdgXa2wDYExjpP0t/zx+GoQgQKzLu7cfD8rHCLt8JrQ==} - engines: {node: '>=6.0'} - - '@oozcitak/url@1.0.0': - resolution: {integrity: sha512-LGrMeSxeLzsdaitxq3ZmBRVOrlRRQIgNNci6L0VRnOKlJFuRIkNm4B+BObXPCJA6JT5bEJtrrwjn30jueHJYZQ==} - engines: {node: '>=8.0'} - - '@oozcitak/util@1.0.1': - resolution: {integrity: sha512-dFwFqcKrQnJ2SapOmRD1nQWEZUtbtIy9Y6TyJquzsalWNJsKIPxmTI0KG6Ypyl8j7v89L2wixH9fQDNrF78hKg==} - engines: {node: '>=6.0'} - - '@oozcitak/util@1.0.2': - resolution: {integrity: sha512-4n8B1cWlJleSOSba5gxsMcN4tO8KkkcvXhNWW+ADqvq9Xj+Lrl9uCa90GRpjekqQJyt84aUX015DG81LFpZYXA==} - engines: {node: '>=6.0'} - - '@oozcitak/util@8.0.0': - resolution: {integrity: sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw==} - engines: {node: '>=6.0'} - - '@oozcitak/util@8.3.3': - resolution: {integrity: sha512-Ufpab7G5PfnEhQyy5kDg9C8ltWJjsVT1P/IYqacjstaqydG4Q21HAT2HUZQYBrC/a1ZLKCz87pfydlDvv8y97w==} - engines: {node: '>=6.0'} - - '@oozcitak/util@8.3.4': - resolution: {integrity: sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==} - engines: {node: '>=8.0'} - '@openrouter/ai-sdk-provider@1.5.4': resolution: {integrity: sha512-xrSQPUIH8n9zuyYZR0XK7Ba0h2KsjJcMkxnwaYfmv13pKs3sDkjPzVPPhlhzqBGddHb5cFEwJ9VFuFeDcxCDSw==} engines: {node: '>=18'} @@ -2550,17 +2516,6 @@ packages: '@react-pdf/types@2.9.2': resolution: {integrity: sha512-dufvpKId9OajLLbgn9q7VLUmyo1Jf+iyGk2ZHmCL8nIDtL8N1Ejh9TH7+pXXrR0tdie1nmnEb5Bz9U7g4hI4/g==} - '@reduxjs/toolkit@2.11.2': - resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 || ^19 - react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -2921,9 +2876,6 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@standard-schema/utils@0.3.0': - resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@swc/helpers@0.5.18': resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} @@ -3221,9 +3173,18 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -3335,6 +3296,12 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + '@types/fs-extra@9.0.13': resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} @@ -3347,6 +3314,9 @@ packages: '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -3398,6 +3368,12 @@ packages: '@types/pdf-parse@1.1.5': resolution: {integrity: sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==} + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -3409,6 +3385,12 @@ packages: '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -3617,8 +3599,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - ai@5.0.133: - resolution: {integrity: sha512-N6KnwSWKcXEWPnAri3anRuzRvcrvtDz1W1JG9CvMrQ0Xdp8Vu8ZToNW/eHt63CmrbmzTwVw/HaCtJuO+MYtS7A==} + ai@5.0.151: + resolution: {integrity: sha512-tsLIv+QN9wJ/xl/fnYgjRYoGSThdnOfU4d6+7QEUKX3EcwviWMEaL1gOE+zfdkcay/Tbc02ZBtTRHoulS6DYvQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -3793,9 +3775,6 @@ packages: brotli@1.3.3: resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} - browser-split@0.0.1: - resolution: {integrity: sha512-JhvgRb2ihQhsljNda3BI8/UcRHVzrVwo3Q+P8vDtSiyobXuFpuZ9mq+MbRGMnC22CjW3RrfXdg6j6ITX8M+7Ow==} - browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} @@ -3857,9 +3836,6 @@ packages: camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - camelize@1.0.1: - resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} - caniuse-lite@1.0.30001761: resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} @@ -4057,8 +4033,8 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} cose-base@1.0.3: @@ -4287,9 +4263,6 @@ packages: supports-color: optional: true - decimal.js-light@2.5.1: - resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} @@ -4358,24 +4331,12 @@ packages: dir-compare@4.2.0: resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==} - dom-serializer@0.2.2: - resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - dom-walk@0.1.2: - resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - - domelementtype@1.3.1: - resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} - domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - domhandler@2.4.2: - resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} - domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} @@ -4383,9 +4344,6 @@ packages: dompurify@3.3.1: resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} - domutils@1.7.0: - resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} - domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -4470,16 +4428,6 @@ packages: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} - ent@2.2.2: - resolution: {integrity: sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==} - engines: {node: '>= 0.4'} - - entities@1.1.2: - resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} - - entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -4498,9 +4446,6 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - error@4.4.0: - resolution: {integrity: sha512-SNDKualLUtT4StGFP7xNfuFybL2f6iJujFtrWuvJqGbVQGaN+adE23veqzPz1hjUjTunLi2EnJ+0SJxtbJreKw==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -4520,9 +4465,6 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-toolkit@1.45.1: - resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} - es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -4623,9 +4565,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - ev-store@7.0.0: - resolution: {integrity: sha512-otazchNRnGzp2YarBJ+GXKVGvhxVATB1zmaStxJBYet0Dyq7A9VhH8IUEB/gRcL6Ch52lfpgPTRJ2m49epyMsQ==} - event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4945,7 +4884,6 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@13.0.0: @@ -4954,7 +4892,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} @@ -4969,9 +4907,6 @@ packages: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} - global@4.4.0: - resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -5115,24 +5050,12 @@ packages: hsl-to-rgb-for-reals@1.1.1: resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==} - html-entities@2.6.0: - resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} - - html-to-docx@1.8.0: - resolution: {integrity: sha512-IiMBWIqXM4+cEsW//RKoonWV7DlXAJBmmKI73XJSVWTIXjGUaxSr2ck1jqzVRZknpvO8xsFnVicldKVAWrBYBA==} - - html-to-vdom@0.7.0: - resolution: {integrity: sha512-k+d2qNkbx0JO00KezQsNcn6k2I/xSBP4yXYFLvXbcasTTDh+RDLUJS3puxqyNnpdyXWRHFGoKU7cRmby8/APcQ==} - html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - htmlparser2@3.10.1: - resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} - http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -5190,23 +5113,9 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - image-size@1.2.1: - resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} - engines: {node: '>=16.x'} - hasBin: true - - image-to-base64@2.2.0: - resolution: {integrity: sha512-Z+aMwm/91UOQqHhrz7Upre2ytKhWejZlWV/JxUTD1sT7GWWKFDJUEV5scVQKnkzSgPHFuQBUEWcanO+ma0PSVw==} - immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immer@10.2.0: - resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} - - immer@11.1.4: - resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -5223,9 +5132,6 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - individual@3.0.0: - resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} - infer-owner@1.0.4: resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} @@ -5326,9 +5232,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-object@1.0.2: - resolution: {integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -5339,10 +5242,6 @@ packages: is-property@1.0.2: resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -5937,9 +5836,6 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - min-document@2.19.2: - resolution: {integrity: sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==} - minimatch@10.1.1: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} @@ -6069,9 +5965,6 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next-tick@0.2.2: - resolution: {integrity: sha512-f7h4svPtl+QidoBv4taKXUjJ70G2asaZ8G28nS0OkqaalX8dwwrtWtyxEDPK62AC00ur/+/E0pUwBwY5EPn15Q==} - nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} @@ -6532,9 +6425,6 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -6594,18 +6484,6 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-redux@9.2.0: - resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} - peerDependencies: - '@types/react': ^18.2.25 || ^19 - react: ^18.0 || ^19 - redux: ^5.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - redux: - optional: true - react-refresh@0.18.0: resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} @@ -6671,26 +6549,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - recharts@3.8.0: - resolution: {integrity: sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==} - engines: {node: '>=18'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - rechoir@0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} - redux-thunk@3.1.0: - resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} - peerDependencies: - redux: ^5.0.0 - - redux@5.0.1: - resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -6763,9 +6625,6 @@ packages: resolution: {integrity: sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==} engines: {node: '>=14', npm: '>=7'} - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -6854,10 +6713,6 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7053,9 +6908,6 @@ packages: peerDependencies: react: ^18.0.0 || ^19.0.0 - string-template@0.2.1: - resolution: {integrity: sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -7141,7 +6993,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me temp@0.9.4: resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==} @@ -7174,9 +7026,6 @@ packages: tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -7306,8 +7155,8 @@ packages: ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - underscore@1.13.7: - resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -7448,12 +7297,6 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - victory-vendor@37.3.6: - resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - - virtual-dom@2.1.1: - resolution: {integrity: sha512-wb6Qc9Lbqug0kRqo/iuApfBpJJAq14Sk1faAnSmtqXiwahg7PVTvWMs9L02Z8nNIMqbwsxzBAA90bbtRLbw0zg==} - vite-compatible-readable-stream@3.6.1: resolution: {integrity: sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==} engines: {node: '>= 6'} @@ -7603,21 +7446,11 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - x-is-array@0.1.0: - resolution: {integrity: sha512-goHPif61oNrr0jJgsXRfc8oqtYzvfiMJpTqwE7Z4y9uH+T3UozkGqQ4d2nX9mB9khvA8U2o/UbPOFjgC7hLWIA==} - - x-is-string@0.1.0: - resolution: {integrity: sha512-GojqklwG8gpzOVEVki5KudKNoq7MbbjYZCbyWzEz7tyPA7eleiE0+ePwOWQQRb5fm86rD3S8Tc0tSFf3AOv50w==} - xlsx@0.18.5: resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} engines: {node: '>=0.8'} hasBin: true - xmlbuilder2@2.1.2: - resolution: {integrity: sha512-PI710tmtVlQ5VmwzbRTuhmVhKnj9pM8Si+iOZCV2g2SNo3gCrpzR2Ka9wNzZtqfD+mnP+xkrqoNy0sjKZqP4Dg==} - engines: {node: '>=8.0'} - xmlbuilder@10.1.1: resolution: {integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==} engines: {node: '>=4.0'} @@ -7694,10 +7527,10 @@ packages: snapshots: - '@ai-sdk/anthropic@2.0.63(zod@4.2.1)': + '@ai-sdk/anthropic@2.0.70(zod@4.2.1)': dependencies: '@ai-sdk/provider': 2.0.1 - '@ai-sdk/provider-utils': 3.0.21(zod@4.2.1) + '@ai-sdk/provider-utils': 3.0.22(zod@4.2.1) zod: 4.2.1 '@ai-sdk/gateway@2.0.24(zod@4.2.1)': @@ -7707,29 +7540,29 @@ snapshots: '@vercel/oidc': 3.0.5 zod: 4.2.1 - '@ai-sdk/gateway@2.0.39(zod@4.2.1)': + '@ai-sdk/gateway@2.0.56(zod@4.2.1)': dependencies: '@ai-sdk/provider': 2.0.1 - '@ai-sdk/provider-utils': 3.0.21(zod@4.2.1) + '@ai-sdk/provider-utils': 3.0.22(zod@4.2.1) '@vercel/oidc': 3.1.0 zod: 4.2.1 - '@ai-sdk/google@2.0.53(zod@4.2.1)': + '@ai-sdk/google@2.0.61(zod@4.2.1)': dependencies: '@ai-sdk/provider': 2.0.1 - '@ai-sdk/provider-utils': 3.0.21(zod@4.2.1) + '@ai-sdk/provider-utils': 3.0.22(zod@4.2.1) zod: 4.2.1 - '@ai-sdk/openai-compatible@1.0.33(zod@4.2.1)': + '@ai-sdk/openai-compatible@1.0.34(zod@4.2.1)': dependencies: '@ai-sdk/provider': 2.0.1 - '@ai-sdk/provider-utils': 3.0.21(zod@4.2.1) + '@ai-sdk/provider-utils': 3.0.22(zod@4.2.1) zod: 4.2.1 - '@ai-sdk/openai@2.0.91(zod@4.2.1)': + '@ai-sdk/openai@2.0.99(zod@4.2.1)': dependencies: '@ai-sdk/provider': 2.0.1 - '@ai-sdk/provider-utils': 3.0.21(zod@4.2.1) + '@ai-sdk/provider-utils': 3.0.22(zod@4.2.1) zod: 4.2.1 '@ai-sdk/provider-utils@3.0.20(zod@4.2.1)': @@ -7739,7 +7572,7 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.2.1 - '@ai-sdk/provider-utils@3.0.21(zod@4.2.1)': + '@ai-sdk/provider-utils@3.0.22(zod@4.2.1)': dependencies: '@ai-sdk/provider': 2.0.1 '@standard-schema/spec': 1.1.0 @@ -9240,7 +9073,7 @@ snapshots: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 - cors: 2.8.5 + cors: 2.8.6 cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 @@ -9397,45 +9230,10 @@ snapshots: dependencies: '@octokit/openapi-types': 12.11.0 - '@oozcitak/dom@1.15.5': - dependencies: - '@oozcitak/infra': 1.0.5 - '@oozcitak/url': 1.0.0 - '@oozcitak/util': 8.0.0 - - '@oozcitak/dom@1.15.6': - dependencies: - '@oozcitak/infra': 1.0.5 - '@oozcitak/url': 1.0.0 - '@oozcitak/util': 8.3.4 - - '@oozcitak/infra@1.0.3': - dependencies: - '@oozcitak/util': 1.0.1 - - '@oozcitak/infra@1.0.5': - dependencies: - '@oozcitak/util': 8.0.0 - - '@oozcitak/url@1.0.0': - dependencies: - '@oozcitak/infra': 1.0.3 - '@oozcitak/util': 1.0.2 - - '@oozcitak/util@1.0.1': {} - - '@oozcitak/util@1.0.2': {} - - '@oozcitak/util@8.0.0': {} - - '@oozcitak/util@8.3.3': {} - - '@oozcitak/util@8.3.4': {} - - '@openrouter/ai-sdk-provider@1.5.4(ai@5.0.133(zod@4.2.1))(zod@4.2.1)': + '@openrouter/ai-sdk-provider@1.5.4(ai@5.0.151(zod@4.2.1))(zod@4.2.1)': dependencies: '@openrouter/sdk': 0.1.27 - ai: 5.0.133(zod@4.2.1) + ai: 5.0.151(zod@4.2.1) zod: 4.2.1 '@openrouter/sdk@0.1.27': @@ -10461,18 +10259,6 @@ snapshots: '@react-pdf/primitives': 4.1.1 '@react-pdf/stylesheet': 6.1.2 - '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3)': - dependencies: - '@standard-schema/spec': 1.1.0 - '@standard-schema/utils': 0.3.0 - immer: 11.1.4 - redux: 5.0.1 - redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.1.1 - optionalDependencies: - react: 19.2.3 - react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1) - '@remirror/core-constants@3.0.0': {} '@rolldown/pluginutils@1.0.0-beta.53': {} @@ -10918,8 +10704,6 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@standard-schema/utils@0.3.0': {} - '@swc/helpers@0.5.18': dependencies: tslib: 2.8.1 @@ -11232,6 +11016,11 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 25.0.3 + '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 @@ -11239,6 +11028,14 @@ snapshots: '@types/node': 25.0.3 '@types/responselike': 1.0.3 + '@types/connect@3.4.38': + dependencies: + '@types/node': 25.0.3 + + '@types/cors@2.8.19': + dependencies: + '@types/node': 25.0.3 + '@types/d3-array@3.2.2': {} '@types/d3-axis@3.0.6': @@ -11378,6 +11175,19 @@ snapshots: '@types/estree@1.0.8': {} + '@types/express-serve-static-core@5.1.1': + dependencies: + '@types/node': 25.0.3 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@5.0.6': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-static': 2.2.0 + '@types/fs-extra@9.0.13': dependencies: '@types/node': 25.0.3 @@ -11391,6 +11201,8 @@ snapshots: '@types/http-cache-semantics@4.0.4': {} + '@types/http-errors@2.0.5': {} + '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {} @@ -11447,6 +11259,10 @@ snapshots: dependencies: '@types/node': 25.0.3 + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 @@ -11459,6 +11275,15 @@ snapshots: dependencies: '@types/node': 25.0.3 + '@types/send@1.2.1': + dependencies: + '@types/node': 25.0.3 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 25.0.3 + '@types/trusted-types@2.0.7': optional: true @@ -11718,11 +11543,11 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.2.1 - ai@5.0.133(zod@4.2.1): + ai@5.0.151(zod@4.2.1): dependencies: - '@ai-sdk/gateway': 2.0.39(zod@4.2.1) + '@ai-sdk/gateway': 2.0.56(zod@4.2.1) '@ai-sdk/provider': 2.0.1 - '@ai-sdk/provider-utils': 3.0.21(zod@4.2.1) + '@ai-sdk/provider-utils': 3.0.22(zod@4.2.1) '@opentelemetry/api': 1.9.0 zod: 4.2.1 @@ -11904,8 +11729,6 @@ snapshots: dependencies: base64-js: 1.5.1 - browser-split@0.0.1: {} - browserify-zlib@0.2.0: dependencies: pako: 1.0.11 @@ -12000,8 +11823,6 @@ snapshots: pascal-case: 3.1.2 tslib: 2.8.1 - camelize@1.0.1: {} - caniuse-lite@1.0.30001761: {} ccount@2.0.1: {} @@ -12173,7 +11994,7 @@ snapshots: core-util-is@1.0.3: {} - cors@2.8.5: + cors@2.8.6: dependencies: object-assign: 4.1.1 vary: 1.1.2 @@ -12422,8 +12243,6 @@ snapshots: dependencies: ms: 2.1.3 - decimal.js-light@2.5.1: {} - decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -12487,27 +12306,14 @@ snapshots: minimatch: 3.1.2 p-limit: 3.1.0 - dom-serializer@0.2.2: - dependencies: - domelementtype: 2.3.0 - entities: 2.2.0 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 - dom-walk@0.1.2: {} - - domelementtype@1.3.1: {} - domelementtype@2.3.0: {} - domhandler@2.4.2: - dependencies: - domelementtype: 1.3.1 - domhandler@5.0.3: dependencies: domelementtype: 2.3.0 @@ -12516,11 +12322,6 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 - domutils@1.7.0: - dependencies: - dom-serializer: 0.2.2 - domelementtype: 1.3.1 - domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -12536,7 +12337,7 @@ snapshots: duck@0.1.12: dependencies: - underscore: 1.13.7 + underscore: 1.13.8 dunder-proto@1.0.1: dependencies: @@ -12661,17 +12462,6 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 - ent@2.2.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - punycode: 1.4.1 - safe-regex-test: 1.1.0 - - entities@1.1.2: {} - - entities@2.2.0: {} - entities@4.5.0: {} entities@6.0.1: {} @@ -12684,12 +12474,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - error@4.4.0: - dependencies: - camelize: 1.0.1 - string-template: 0.2.1 - xtend: 4.0.2 - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -12707,8 +12491,6 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - es-toolkit@1.45.1: {} - es6-error@4.1.1: optional: true @@ -12873,10 +12655,6 @@ snapshots: etag@1.8.1: {} - ev-store@7.0.0: - dependencies: - individual: 3.0.0 - event-target-shim@5.0.1: {} eventemitter3@5.0.1: {} @@ -13320,11 +13098,6 @@ snapshots: dependencies: ini: 2.0.0 - global@4.4.0: - dependencies: - min-document: 2.19.2 - process: 0.11.10 - globals@14.0.0: {} globals@16.5.0: {} @@ -13571,44 +13344,10 @@ snapshots: hsl-to-rgb-for-reals@1.1.1: {} - html-entities@2.6.0: {} - - html-to-docx@1.8.0(encoding@0.1.13): - dependencies: - '@oozcitak/dom': 1.15.6 - '@oozcitak/util': 8.3.4 - color-name: 1.1.4 - html-entities: 2.6.0 - html-to-vdom: 0.7.0 - image-size: 1.2.1 - image-to-base64: 2.2.0(encoding@0.1.13) - jszip: 3.10.1 - lodash: 4.17.21 - mime-types: 2.1.35 - nanoid: 3.3.11 - virtual-dom: 2.1.1 - xmlbuilder2: 2.1.2 - transitivePeerDependencies: - - encoding - - html-to-vdom@0.7.0: - dependencies: - ent: 2.2.2 - htmlparser2: 3.10.1 - html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} - htmlparser2@3.10.1: - dependencies: - domelementtype: 1.3.1 - domhandler: 2.4.2 - domutils: 1.7.0 - entities: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - http-cache-semantics@4.2.0: {} http-errors@2.0.1: @@ -13673,22 +13412,8 @@ snapshots: image-size@0.7.5: optional: true - image-size@1.2.1: - dependencies: - queue: 6.0.2 - - image-to-base64@2.2.0(encoding@0.1.13): - dependencies: - node-fetch: 2.7.0(encoding@0.1.13) - transitivePeerDependencies: - - encoding - immediate@3.0.6: {} - immer@10.2.0: {} - - immer@11.1.4: {} - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -13701,8 +13426,6 @@ snapshots: indent-string@4.0.0: {} - individual@3.0.0: {} - infer-owner@1.0.4: {} inflight@1.0.6: @@ -13777,8 +13500,6 @@ snapshots: is-number@7.0.0: {} - is-object@1.0.2: {} - is-plain-obj@4.1.0: {} is-promise@4.0.0: {} @@ -13786,13 +13507,6 @@ snapshots: is-property@1.0.2: optional: true - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - is-stream@1.1.0: {} is-stream@2.0.1: {} @@ -14078,7 +13792,7 @@ snapshots: dependencies: duck: 0.1.12 option: 0.2.4 - underscore: 1.13.7 + underscore: 1.13.8 lower-case@2.0.2: dependencies: @@ -14147,7 +13861,7 @@ snapshots: jszip: 3.10.1 lop: 0.4.2 path-is-absolute: 1.0.1 - underscore: 1.13.7 + underscore: 1.13.8 xmlbuilder: 10.1.1 map-age-cleaner@0.1.3: @@ -14638,10 +14352,6 @@ snapshots: mimic-response@3.1.0: {} - min-document@2.19.2: - dependencies: - dom-walk: 0.1.2 - minimatch@10.1.1: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -14757,8 +14467,6 @@ snapshots: neo-async@2.6.2: {} - next-tick@0.2.2: {} - nice-try@1.0.5: {} no-case@3.0.4: @@ -15247,8 +14955,6 @@ snapshots: punycode.js@2.3.1: {} - punycode@1.4.1: {} - punycode@2.3.1: {} pusher-js@8.4.0: @@ -15358,15 +15064,6 @@ snapshots: react-is@16.13.1: {} - react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1): - dependencies: - '@types/use-sync-external-store': 0.0.6 - react: 19.2.3 - use-sync-external-store: 1.6.0(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.7 - redux: 5.0.1 - react-refresh@0.18.0: {} react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.3): @@ -15441,36 +15138,10 @@ snapshots: readdirp@4.1.2: {} - recharts@3.8.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@16.13.1)(react@19.2.3)(redux@5.0.1): - dependencies: - '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3) - clsx: 2.1.1 - decimal.js-light: 2.5.1 - es-toolkit: 1.45.1 - eventemitter3: 5.0.1 - immer: 10.2.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - react-is: 16.13.1 - react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1) - reselect: 5.1.1 - tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.2.3) - victory-vendor: 37.3.6 - transitivePeerDependencies: - - '@types/react' - - redux - rechoir@0.8.0: dependencies: resolve: 1.22.11 - redux-thunk@3.1.0(redux@5.0.1): - dependencies: - redux: 5.0.1 - - redux@5.0.1: {} - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -15577,8 +15248,6 @@ snapshots: dependencies: pe-library: 1.0.1 - reselect@5.1.1: {} - resolve-alpn@1.2.1: {} resolve-from@4.0.0: {} @@ -15697,12 +15366,6 @@ snapshots: safe-buffer@5.2.1: {} - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - safer-buffer@2.1.2: {} scheduler@0.25.0-rc-603e6108-20241029: {} @@ -15953,8 +15616,6 @@ snapshots: - micromark-util-types - supports-color - string-template@0.2.1: {} - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -16072,8 +15733,6 @@ snapshots: tiny-inflate@1.0.3: {} - tiny-invariant@1.3.3: {} - tinyexec@1.0.2: {} tinyglobby@0.2.15: @@ -16194,7 +15853,7 @@ snapshots: ufo@1.6.1: {} - underscore@1.13.7: {} + underscore@1.13.8: {} undici-types@6.21.0: {} @@ -16345,34 +16004,6 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - victory-vendor@37.3.6: - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-ease': 3.0.2 - '@types/d3-interpolate': 3.0.4 - '@types/d3-scale': 4.0.9 - '@types/d3-shape': 3.1.7 - '@types/d3-time': 3.0.4 - '@types/d3-timer': 3.0.2 - d3-array: 3.2.4 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-scale: 4.0.2 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-timer: 3.0.1 - - virtual-dom@2.1.1: - dependencies: - browser-split: 0.0.1 - error: 4.4.0 - ev-store: 7.0.0 - global: 4.4.0 - is-object: 1.0.2 - next-tick: 0.2.2 - x-is-array: 0.1.0 - x-is-string: 0.1.0 - vite-compatible-readable-stream@3.6.1: dependencies: inherits: 2.0.4 @@ -16524,10 +16155,6 @@ snapshots: wrappy@1.0.2: {} - x-is-array@0.1.0: {} - - x-is-string@0.1.0: {} - xlsx@0.18.5: dependencies: adler-32: 1.3.1 @@ -16538,17 +16165,12 @@ snapshots: wmf: 1.0.2 word: 0.3.0 - xmlbuilder2@2.1.2: - dependencies: - '@oozcitak/dom': 1.15.5 - '@oozcitak/infra': 1.0.5 - '@oozcitak/util': 8.3.3 - xmlbuilder@10.1.1: {} xmlbuilder@15.1.1: {} - xtend@4.0.2: {} + xtend@4.0.2: + optional: true y18n@5.0.8: {}