diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index b0757881..4d272275 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -31,6 +31,7 @@ import { IAgentScheduleRepo } from '@x/core/dist/agent-schedule/repo.js'; import { IAgentScheduleStateRepo } from '@x/core/dist/agent-schedule/state-repo.js'; import { triggerRun as triggerAgentScheduleRun } from '@x/core/dist/agent-schedule/runner.js'; import { search } from '@x/core/dist/search/search.js'; +import { versionHistory } from '@x/core'; type InvokeChannels = ipc.InvokeChannels; type IPCChannels = ipc.IPCChannels; @@ -105,6 +106,18 @@ let watcher: FSWatcher | null = null; const changeQueue = new Set(); let debounceTimer: ReturnType | null = null; +/** + * Emit knowledge commit event to all renderer windows + */ +function emitKnowledgeCommitEvent(): void { + const windows = BrowserWindow.getAllWindows(); + for (const win of windows) { + if (!win.isDestroyed() && win.webContents) { + win.webContents.send('knowledge:didCommit', {}); + } + } +} + /** * Emit workspace change event to all renderer windows */ @@ -283,6 +296,9 @@ export function stopServicesWatcher(): void { * Add new handlers here as you add channels to IPCChannels */ export function setupIpcHandlers() { + // Forward knowledge commit events to renderer for panel refresh + versionHistory.onCommit(() => emitKnowledgeCommitEvent()); + registerIpcHandlers({ 'app:getVersions': async () => { // args is null for this channel (no request payload) @@ -498,6 +514,19 @@ export function setupIpcHandlers() { const mimeType = mimeMap[ext] || 'application/octet-stream'; return { data: buffer.toString('base64'), mimeType, size: stat.size }; }, + // Knowledge version history handlers + 'knowledge:history': async (_event, args) => { + const commits = await versionHistory.getFileHistory(args.path); + return { commits }; + }, + 'knowledge:fileAtCommit': async (_event, args) => { + const content = await versionHistory.getFileAtCommit(args.path, args.oid); + return { content }; + }, + 'knowledge:restore': async (_event, args) => { + await versionHistory.restoreFile(args.path, args.oid); + return { ok: true }; + }, // Search handler 'search:query': async (_event, args) => { return search(args.query, args.limit, args.types); diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 0ee4fa5d..af6a4740 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -5,7 +5,7 @@ import { RunEvent, ListRunsResponse } from '@x/shared/src/runs.js'; import type { LanguageModelUsage, ToolUIPart } from 'ai'; import './App.css' import z from 'zod'; -import { CheckIcon, LoaderIcon, PanelLeftIcon, Maximize2, Minimize2, ChevronLeftIcon, ChevronRightIcon, SquarePen, SearchIcon } from 'lucide-react'; +import { CheckIcon, LoaderIcon, PanelLeftIcon, Maximize2, Minimize2, ChevronLeftIcon, ChevronRightIcon, SquarePen, SearchIcon, HistoryIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; import { MarkdownEditor } from './components/markdown-editor'; import { ChatSidebar } from './components/chat-sidebar'; @@ -49,6 +49,7 @@ import { stripKnowledgePrefix, toKnowledgePath, wikiLabel } from '@/lib/wiki-lin import { OnboardingModal } from '@/components/onboarding-modal' import { SearchDialog } from '@/components/search-dialog' import { BackgroundTaskDetail } from '@/components/background-task-detail' +import { VersionHistoryPanel } from '@/components/version-history-panel' import { FileCardProvider } from '@/contexts/file-card-context' import { MarkdownPreOverride } from '@/components/ai-elements/markdown-code-override' import { TabBar, type ChatTab, type FileTab } from '@/components/tab-bar' @@ -506,6 +507,13 @@ function App() { const initialContentRef = useRef('') const renameInProgressRef = useRef(false) + // Version history state + const [versionHistoryPath, setVersionHistoryPath] = useState(null) + const [viewingHistoricalVersion, setViewingHistoricalVersion] = useState<{ + oid: string + content: string + } | null>(null) + // Chat state const [, setMessage] = useState('') const [conversation, setConversation] = useState([]) @@ -1072,6 +1080,14 @@ function App() { saveFile() }, [debouncedContent, setHistory]) + // Close version history panel when switching files + useEffect(() => { + if (versionHistoryPath && selectedPath !== versionHistoryPath) { + setVersionHistoryPath(null) + setViewingHistoricalVersion(null) + } + }, [selectedPath, versionHistoryPath]) + // Load runs list (all pages) const loadRuns = useCallback(async () => { try { @@ -3213,6 +3229,31 @@ function App() { ) : null} )} + {selectedPath && selectedPath.startsWith('knowledge/') && selectedPath.endsWith('.md') && ( + + + + + Version history + + )} {!selectedPath && !isGraphOpen && !selectedTask && ( @@ -3276,41 +3317,80 @@ function App() { ) : selectedPath ? ( selectedPath.endsWith('.md') ? ( -
- {openMarkdownTabs.map((tab) => { - const isActive = activeFileTabId - ? tab.id === activeFileTabId || tab.path === selectedPath - : tab.path === selectedPath - const tabContent = editorContentByPath[tab.path] - ?? (isActive && editorPathRef.current === tab.path ? editorContent : '') - return ( -
- handleEditorChange(tab.path, markdown)} - placeholder="Start writing..." - wikiLinks={wikiLinkConfig} - onImageUpload={handleImageUpload} - editorSessionKey={editorSessionByTabId[tab.id] ?? 0} - onHistoryHandlersChange={(handlers) => { - if (handlers) { - fileHistoryHandlersRef.current.set(tab.id, handlers) - } else { - fileHistoryHandlersRef.current.delete(tab.id) - } - }} - /> -
- ) - })} +
+
+ {openMarkdownTabs.map((tab) => { + const isActive = activeFileTabId + ? tab.id === activeFileTabId || tab.path === selectedPath + : tab.path === selectedPath + const isViewingHistory = viewingHistoricalVersion && isActive && versionHistoryPath === tab.path + const tabContent = isViewingHistory + ? viewingHistoricalVersion.content + : editorContentByPath[tab.path] + ?? (isActive && editorPathRef.current === tab.path ? editorContent : '') + return ( +
+ { if (!isViewingHistory) handleEditorChange(tab.path, markdown) }} + placeholder="Start writing..." + wikiLinks={wikiLinkConfig} + onImageUpload={handleImageUpload} + editorSessionKey={editorSessionByTabId[tab.id] ?? 0} + onHistoryHandlersChange={(handlers) => { + if (handlers) { + fileHistoryHandlersRef.current.set(tab.id, handlers) + } else { + fileHistoryHandlersRef.current.delete(tab.id) + } + }} + editable={!isViewingHistory} + /> +
+ ) + })} +
+ {versionHistoryPath && ( + { + setVersionHistoryPath(null) + setViewingHistoricalVersion(null) + }} + onSelectVersion={(oid, content) => { + if (oid === null) { + setViewingHistoricalVersion(null) + } else { + setViewingHistoricalVersion({ oid, content }) + } + }} + onRestore={async (oid) => { + try { + await window.ipc.invoke('knowledge:restore', { + path: versionHistoryPath.startsWith('knowledge/') + ? versionHistoryPath.slice('knowledge/'.length) + : versionHistoryPath, + oid, + }) + // Reload file content + const result = await window.ipc.invoke('workspace:readFile', { path: versionHistoryPath }) + handleEditorChange(versionHistoryPath, result.data) + setViewingHistoricalVersion(null) + setVersionHistoryPath(null) + } catch (err) { + console.error('Failed to restore version:', err) + } + }} + /> + )}
) : (
diff --git a/apps/x/apps/renderer/src/components/markdown-editor.tsx b/apps/x/apps/renderer/src/components/markdown-editor.tsx index 2db7bcbe..6bcaef29 100644 --- a/apps/x/apps/renderer/src/components/markdown-editor.tsx +++ b/apps/x/apps/renderer/src/components/markdown-editor.tsx @@ -197,6 +197,7 @@ interface MarkdownEditorProps { onImageUpload?: (file: File) => Promise editorSessionKey?: number onHistoryHandlersChange?: (handlers: { undo: () => boolean; redo: () => boolean } | null) => void + editable?: boolean } type WikiLinkMatch = { @@ -282,6 +283,7 @@ export function MarkdownEditor({ onImageUpload, editorSessionKey = 0, onHistoryHandlersChange, + editable = true, }: MarkdownEditorProps) { const isInternalUpdate = useRef(false) const wrapperRef = useRef(null) @@ -303,6 +305,7 @@ export function MarkdownEditor({ ) const editor = useEditor({ + editable, extensions: [ StarterKit.configure({ heading: { @@ -517,6 +520,13 @@ export function MarkdownEditor({ } }, [editor, onHistoryHandlersChange]) + // Update editable state when prop changes + useEffect(() => { + if (editor) { + editor.setEditable(editable) + } + }, [editor, editable]) + // Force re-render decorations when selection highlight changes useEffect(() => { if (editor) { diff --git a/apps/x/apps/renderer/src/components/version-history-panel.tsx b/apps/x/apps/renderer/src/components/version-history-panel.tsx new file mode 100644 index 00000000..f8d03d38 --- /dev/null +++ b/apps/x/apps/renderer/src/components/version-history-panel.tsx @@ -0,0 +1,177 @@ +import { useEffect, useState, useCallback } from 'react' +import { X, Clock } from 'lucide-react' +import { cn } from '@/lib/utils' +import { Button } from '@/components/ui/button' + +interface CommitInfo { + oid: string + message: string + timestamp: number + author: string +} + +interface VersionHistoryPanelProps { + path: string // knowledge-relative file path (e.g. "knowledge/People/John.md") + onClose: () => void + onSelectVersion: (oid: string | null, content: string) => void // null = current + onRestore: (oid: string) => void +} + +function formatTimestamp(unixSeconds: number): { date: string; time: string } { + const d = new Date(unixSeconds * 1000) + const date = d.toLocaleDateString('en-US', { month: 'long', day: 'numeric' }) + const time = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }) + return { date, time } +} + +export function VersionHistoryPanel({ + path, + onClose, + onSelectVersion, + onRestore, +}: VersionHistoryPanelProps) { + const [commits, setCommits] = useState([]) + const [loading, setLoading] = useState(true) + const [selectedOid, setSelectedOid] = useState(null) // null = current/latest + const [error, setError] = useState(null) + + // Strip "knowledge/" prefix for IPC calls + const relPath = path.startsWith('knowledge/') ? path.slice('knowledge/'.length) : path + + const loadHistory = useCallback(async () => { + setLoading(true) + setError(null) + try { + const result = await window.ipc.invoke('knowledge:history', { path: relPath }) + setCommits(result.commits) + } catch (err) { + console.error('Failed to load version history:', err) + setError('Failed to load history') + } finally { + setLoading(false) + } + }, [relPath]) + + useEffect(() => { + loadHistory() + }, [loadHistory]) + + // Refresh when new commits land + useEffect(() => { + return window.ipc.on('knowledge:didCommit', () => { + loadHistory() + }) + }, [loadHistory]) + + const handleSelectCommit = useCallback(async (oid: string, isLatest: boolean) => { + if (isLatest) { + setSelectedOid(null) + // Read current file content + try { + const result = await window.ipc.invoke('workspace:readFile', { path }) + onSelectVersion(null, result.data) + } catch (err) { + console.error('Failed to read current file:', err) + } + return + } + + setSelectedOid(oid) + try { + const result = await window.ipc.invoke('knowledge:fileAtCommit', { path: relPath, oid }) + onSelectVersion(oid, result.content) + } catch (err) { + console.error('Failed to load file at commit:', err) + } + }, [path, relPath, onSelectVersion]) + + const handleRestore = useCallback(() => { + if (selectedOid) { + onRestore(selectedOid) + } + }, [selectedOid, onRestore]) + + return ( +
+ {/* Header */} +
+ Version history + +
+ + {/* Commit list */} +
+ {loading ? ( +
+ Loading... +
+ ) : error ? ( +
+ {error} +
+ ) : commits.length === 0 ? ( +
+ No history available +
+ ) : ( +
+ {commits.map((commit, index) => { + const isLatest = index === 0 + const isSelected = isLatest ? selectedOid === null : selectedOid === commit.oid + const { date, time } = formatTimestamp(commit.timestamp) + + return ( + + ) + })} +
+ )} +
+ + {/* Footer */} + {selectedOid && ( +
+ +
+ )} +
+ ) +} diff --git a/apps/x/packages/core/package.json b/apps/x/packages/core/package.json index bcfac93d..53495637 100644 --- a/apps/x/packages/core/package.json +++ b/apps/x/packages/core/package.json @@ -27,6 +27,7 @@ "cron-parser": "^5.5.0", "glob": "^13.0.0", "google-auth-library": "^10.5.0", + "isomorphic-git": "^1.29.0", "googleapis": "^169.0.0", "mammoth": "^1.11.0", "node-html-markdown": "^2.0.0", diff --git a/apps/x/packages/core/src/config/config.ts b/apps/x/packages/core/src/config/config.ts index caefad82..4a91e101 100644 --- a/apps/x/packages/core/src/config/config.ts +++ b/apps/x/packages/core/src/config/config.ts @@ -91,4 +91,9 @@ function ensureWelcomeFile() { ensureDirs(); ensureDefaultConfigs(); -ensureWelcomeFile(); \ No newline at end of file +ensureWelcomeFile(); + +// Initialize version history repo (async, fire-and-forget on startup) +import('../knowledge/version_history.js').then(m => m.initRepo()).catch(err => { + console.error('[VersionHistory] Failed to init repo:', err); +}); diff --git a/apps/x/packages/core/src/index.ts b/apps/x/packages/core/src/index.ts index b7718cba..0eab08e3 100644 --- a/apps/x/packages/core/src/index.ts +++ b/apps/x/packages/core/src/index.ts @@ -5,4 +5,7 @@ export * as workspace from './workspace/workspace.js'; export * as watcher from './workspace/watcher.js'; // Config initialization -export { initConfigs } from './config/initConfigs.js'; \ No newline at end of file +export { initConfigs } from './config/initConfigs.js'; + +// Knowledge version history +export * as versionHistory from './knowledge/version_history.js'; diff --git a/apps/x/packages/core/src/knowledge/build_graph.ts b/apps/x/packages/core/src/knowledge/build_graph.ts index a1b7e135..a119dfa6 100644 --- a/apps/x/packages/core/src/knowledge/build_graph.ts +++ b/apps/x/packages/core/src/knowledge/build_graph.ts @@ -15,6 +15,7 @@ import { } from './graph_state.js'; import { buildKnowledgeIndex, formatIndexForPrompt } from './knowledge_index.js'; import { limitEventItems } from './limit_event_items.js'; +import { commitAll } from './version_history.js'; /** * Build obsidian-style knowledge graph by running topic extraction @@ -320,6 +321,13 @@ async function buildGraphWithFiles( // Save state after each successful batch // This ensures partial progress is saved even if later batches fail saveState(state); + + // Commit knowledge changes to version history + try { + await commitAll('Knowledge update', 'Rowboat'); + } catch (err) { + console.error(`[GraphBuilder] Failed to commit version history:`, err); + } } catch (error) { hadError = true; console.error(`Error processing batch ${batchNumber}:`, error); @@ -467,6 +475,13 @@ async function processVoiceMemosForKnowledge(): Promise { // Save state after each batch saveState(state); + + // Commit knowledge changes to version history + try { + await commitAll('Knowledge update', 'Rowboat'); + } catch (err) { + console.error(`[GraphBuilder] Failed to commit version history:`, err); + } } catch (error) { hadError = true; console.error(`[GraphBuilder] Error processing batch ${batchNumber}:`, error); diff --git a/apps/x/packages/core/src/knowledge/version_history.ts b/apps/x/packages/core/src/knowledge/version_history.ts new file mode 100644 index 00000000..a6504f67 --- /dev/null +++ b/apps/x/packages/core/src/knowledge/version_history.ts @@ -0,0 +1,243 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import git from 'isomorphic-git'; +import { WorkDir } from '../config/config.js'; + +const KNOWLEDGE_DIR = path.join(WorkDir, 'knowledge'); + +// Simple promise-based mutex to serialize commits +let commitLock: Promise = Promise.resolve(); + +// Commit listeners for notifying other layers (e.g. renderer refresh) +type CommitListener = () => void; +const commitListeners: CommitListener[] = []; + +export function onCommit(listener: CommitListener): () => void { + commitListeners.push(listener); + return () => { + const idx = commitListeners.indexOf(listener); + if (idx >= 0) commitListeners.splice(idx, 1); + }; +} + +/** + * Initialize a git repo in the knowledge directory if one doesn't exist. + * Stages all existing .md files and makes an initial commit. + */ +export async function initRepo(): Promise { + const gitDir = path.join(KNOWLEDGE_DIR, '.git'); + if (fs.existsSync(gitDir)) { + return; + } + + // Ensure knowledge dir exists + if (!fs.existsSync(KNOWLEDGE_DIR)) { + fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true }); + } + + await git.init({ fs, dir: KNOWLEDGE_DIR }); + + // Stage all existing .md files + const files = getAllMdFiles(KNOWLEDGE_DIR, ''); + for (const file of files) { + await git.add({ fs, dir: KNOWLEDGE_DIR, filepath: file }); + } + + if (files.length > 0) { + await git.commit({ + fs, + dir: KNOWLEDGE_DIR, + message: 'Initial snapshot', + author: { name: 'Rowboat', email: 'local' }, + }); + } +} + +/** + * Recursively find all .md files relative to the knowledge dir. + */ +function getAllMdFiles(baseDir: string, relDir: string): string[] { + const results: string[] = []; + const absDir = relDir ? path.join(baseDir, relDir) : baseDir; + let entries: string[]; + try { + entries = fs.readdirSync(absDir); + } catch { + return results; + } + for (const entry of entries) { + if (entry === '.git' || entry.startsWith('.')) continue; + const fullPath = path.join(absDir, entry); + const relPath = relDir ? `${relDir}/${entry}` : entry; + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + results.push(...getAllMdFiles(baseDir, relPath)); + } else if (entry.endsWith('.md')) { + results.push(relPath); + } + } + return results; +} + +/** + * Stage all changes to .md files and commit. No-op if nothing changed. + * Serialized via a promise lock to prevent concurrent git index corruption. + */ +export async function commitAll(message: string, authorName: string): Promise { + const prev = commitLock; + let resolve: () => void; + commitLock = new Promise(r => { resolve = r; }); + + await prev; + try { + await commitAllInner(message, authorName); + } finally { + resolve!(); + } +} + +async function commitAllInner(message: string, authorName: string): Promise { + const matrix = await git.statusMatrix({ fs, dir: KNOWLEDGE_DIR }); + + let hasChanges = false; + for (const [filepath, head, workdir, stage] of matrix) { + // Skip non-md files + if (!filepath.endsWith('.md')) continue; + + // [filepath, HEAD, WORKDIR, STAGE] + // Unchanged: [f, 1, 1, 1] + if (head === 1 && workdir === 1 && stage === 1) continue; + + hasChanges = true; + + if (workdir === 0) { + // File deleted from workdir + await git.remove({ fs, dir: KNOWLEDGE_DIR, filepath }); + } else { + // File added or modified + await git.add({ fs, dir: KNOWLEDGE_DIR, filepath }); + } + } + + if (!hasChanges) return; + + await git.commit({ + fs, + dir: KNOWLEDGE_DIR, + message, + author: { name: authorName, email: 'local' }, + }); + + for (const listener of commitListeners) { + try { listener(); } catch { /* ignore */ } + } +} + +export interface CommitInfo { + oid: string; + message: string; + timestamp: number; + author: string; +} + +const MAX_FILE_HISTORY = 50; + +/** + * Get commit history for a specific file. + * Returns commits where the file content changed, most recent first. + * Capped at MAX_FILE_HISTORY entries. + */ +export async function getFileHistory(knowledgeRelPath: string): Promise { + // Normalize path separators for git (always forward slashes) + const filepath = knowledgeRelPath.replace(/\\/g, '/'); + + let commits: Awaited>; + try { + commits = await git.log({ fs, dir: KNOWLEDGE_DIR }); + } catch { + return []; + } + + if (commits.length === 0) return []; + + const result: CommitInfo[] = []; + + // Walk through commits and check if file changed between consecutive commits + for (let i = 0; i < commits.length; i++) { + if (result.length >= MAX_FILE_HISTORY) break; + + const commit = commits[i]!; + const parentCommit = commits[i + 1]; // undefined for the first (oldest) commit + + const currentOid = await getBlobOidAtCommit(commit.oid, filepath); + const parentOid = parentCommit + ? await getBlobOidAtCommit(parentCommit.oid, filepath) + : null; + + // Include this commit if: + // - The file existed and changed from parent + // - The file was added (parentOid is null but currentOid exists) + // - The file was deleted (currentOid is null but parentOid exists) + if (currentOid !== parentOid) { + result.push({ + oid: commit.oid, + message: commit.commit.message.trim(), + timestamp: commit.commit.author.timestamp, + author: commit.commit.author.name, + }); + } + } + + return result; +} + +/** + * Get the blob OID for a file at a specific commit, or null if not found. + */ +async function getBlobOidAtCommit(commitOid: string, filepath: string): Promise { + try { + const result = await git.readBlob({ + fs, + dir: KNOWLEDGE_DIR, + oid: commitOid, + filepath, + }); + // Compute a content hash from the blob to compare + return result.oid; + } catch { + return null; + } +} + +/** + * Read file content at a specific commit. + */ +export async function getFileAtCommit(knowledgeRelPath: string, oid: string): Promise { + const filepath = knowledgeRelPath.replace(/\\/g, '/'); + const result = await git.readBlob({ + fs, + dir: KNOWLEDGE_DIR, + oid, + filepath, + }); + return Buffer.from(result.blob).toString('utf-8'); +} + +/** + * Restore a file to its content at a given commit, then commit the restoration. + */ +export async function restoreFile(knowledgeRelPath: string, oid: string): Promise { + const content = await getFileAtCommit(knowledgeRelPath, oid); + const absPath = path.join(KNOWLEDGE_DIR, knowledgeRelPath); + + // Ensure parent directory exists + const dir = path.dirname(absPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + fs.writeFileSync(absPath, content, 'utf-8'); + + const filename = path.basename(knowledgeRelPath); + await commitAll(`Restored ${filename}`, 'You'); +} diff --git a/apps/x/packages/core/src/workspace/workspace.ts b/apps/x/packages/core/src/workspace/workspace.ts index cd872f39..de1fe212 100644 --- a/apps/x/packages/core/src/workspace/workspace.ts +++ b/apps/x/packages/core/src/workspace/workspace.ts @@ -6,6 +6,7 @@ import { z } from 'zod'; import { RemoveOptions, WriteFileOptions, WriteFileResult } from 'packages/shared/dist/workspace.js'; import { WorkDir } from '../config/config.js'; import { rewriteWikiLinksForRenamedKnowledgeFile } from './wiki-link-rewrite.js'; +import { commitAll } from '../knowledge/version_history.js'; // ============================================================================ // Path Utilities @@ -218,6 +219,21 @@ export async function readFile( }; } +// Debounced commit for knowledge file edits +let knowledgeCommitTimer: ReturnType | null = null; + +function scheduleKnowledgeCommit(filename: string): void { + if (knowledgeCommitTimer) { + clearTimeout(knowledgeCommitTimer); + } + knowledgeCommitTimer = setTimeout(() => { + knowledgeCommitTimer = null; + commitAll(`Edit ${filename}`, 'You').catch(err => { + console.error('[VersionHistory] Failed to commit after edit:', err); + }); + }, 3 * 60 * 1000); +} + export async function writeFile( relPath: string, data: string, @@ -266,6 +282,11 @@ export async function writeFile( const stat = statToSchema(stats, 'file'); const etag = computeEtag(stats.size, stats.mtimeMs); + // Schedule a debounced version history commit for knowledge files + if (relPath.startsWith('knowledge/') && relPath.endsWith('.md')) { + scheduleKnowledgeCommit(path.basename(relPath)); + } + return { path: relPath, stat, diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts index c0276d9a..b5803ffc 100644 --- a/apps/x/packages/shared/src/ipc.ts +++ b/apps/x/packages/shared/src/ipc.ts @@ -397,6 +397,30 @@ const ipcSchemas = { req: z.object({ path: z.string() }), res: z.object({ data: z.string(), mimeType: z.string(), size: z.number() }), }, + // Knowledge version history channels + 'knowledge:history': { + req: z.object({ path: RelPath }), + res: z.object({ + commits: z.array(z.object({ + oid: z.string(), + message: z.string(), + timestamp: z.number(), + author: z.string(), + })), + }), + }, + 'knowledge:fileAtCommit': { + req: z.object({ path: RelPath, oid: z.string() }), + res: z.object({ content: z.string() }), + }, + 'knowledge:restore': { + req: z.object({ path: RelPath, oid: z.string() }), + res: z.object({ ok: z.literal(true) }), + }, + 'knowledge:didCommit': { + req: z.object({}), + res: z.null(), + }, // Search channels 'search:query': { req: z.object({ diff --git a/apps/x/pnpm-lock.yaml b/apps/x/pnpm-lock.yaml index fa8765d6..d3525c4f 100644 --- a/apps/x/pnpm-lock.yaml +++ b/apps/x/pnpm-lock.yaml @@ -359,6 +359,9 @@ importers: googleapis: specifier: ^169.0.0 version: 169.0.0 + isomorphic-git: + specifier: ^1.29.0 + version: 1.37.2 mammoth: specifier: ^1.11.0 version: 1.11.0 @@ -3501,6 +3504,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + abs-svg-path@0.1.1: resolution: {integrity: sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==} @@ -3627,6 +3634,9 @@ packages: resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} engines: {node: '>=8'} + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + async@1.5.2: resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} @@ -3641,6 +3651,10 @@ packages: resolution: {integrity: sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==} engines: {node: '>=0.8'} + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + awilix@12.0.5: resolution: {integrity: sha512-Qf/V/hRo6DK0FoBKJ9QiObasRxHAhcNi0mV6kW2JMawxS3zq6Un+VsZmVAZDUfvB+MjTEiJ2tUJUl4cr0JiUAw==} engines: {node: '>=16.3.0'} @@ -3742,6 +3756,9 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -3762,6 +3779,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -3825,6 +3846,9 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + clean-git-ref@2.0.1: + resolution: {integrity: sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -4256,6 +4280,9 @@ packages: dfa@1.2.0: resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + diff3@0.0.3: + resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==} + dingbat-to-unicode@1.0.1: resolution: {integrity: sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==} @@ -4496,6 +4523,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -4640,6 +4671,10 @@ packages: fontkit@2.0.4: resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -5104,6 +5139,10 @@ packages: is-arrayish@0.3.4: resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -5170,6 +5209,10 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -5184,6 +5227,9 @@ packages: isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isbinaryfile@4.0.10: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} @@ -5191,6 +5237,11 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-git@1.37.2: + resolution: {integrity: sha512-HCQBBKmXIMPdHgYGstSBNp6MNmVcMQBbUqJF8xfywFmlpNseO4KKex59YlXqNxhRxmv3fUZwvNWvMyOdc1VvhA==} + engines: {node: '>=14.17'} + hasBin: true + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -5762,6 +5813,9 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimisted@2.0.1: + resolution: {integrity: sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==} + minipass-collect@1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} engines: {node: '>= 8'} @@ -6169,6 +6223,10 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -6186,6 +6244,10 @@ packages: points-on-path@0.2.1: resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -6220,6 +6282,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -6434,6 +6500,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -6649,12 +6719,21 @@ packages: server-destroy@1.0.1: resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -6701,6 +6780,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-swizzle@0.2.4: resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} @@ -6928,6 +7013,10 @@ packages: resolution: {integrity: sha512-DbplOfQFkqG5IHcDyyrs/lkvSr3mPUVsFf/RbDppOshs22yTPnSJWEe6FkYd1txAwU/zcnR905ar2fi4kwF29w==} engines: {node: '>=0.12'} + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + to-data-view@1.1.0: resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==} @@ -6998,6 +7087,10 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + typescript-eslint@8.50.1: resolution: {integrity: sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -7272,6 +7365,10 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -11316,6 +11413,10 @@ snapshots: abbrev@1.1.1: {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + abs-svg-path@0.1.1: {} accepts@2.0.0: @@ -11440,6 +11541,8 @@ snapshots: arrify@2.0.1: {} + async-lock@1.4.1: {} + async@1.5.2: optional: true @@ -11449,6 +11552,10 @@ snapshots: author-regex@1.0.0: {} + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + awilix@12.0.5: dependencies: camel-case: 4.1.2 @@ -11568,6 +11675,11 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bytes@3.1.2: {} cacache@16.1.3: @@ -11610,6 +11722,13 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -11672,6 +11791,8 @@ snapshots: dependencies: clsx: 2.1.1 + clean-git-ref@2.0.1: {} + clean-stack@2.2.0: {} cli-cursor@3.1.0: @@ -12061,7 +12182,6 @@ snapshots: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - optional: true define-properties@1.2.1: dependencies: @@ -12095,6 +12215,8 @@ snapshots: dfa@1.2.0: {} + diff3@0.0.3: {} + dingbat-to-unicode@1.0.1: {} dir-compare@4.2.0: @@ -12451,6 +12573,8 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + eventemitter3@5.0.1: {} events@3.3.0: {} @@ -12638,6 +12762,10 @@ snapshots: unicode-properties: 1.4.1 unicode-trie: 2.0.0 + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -12983,7 +13111,6 @@ snapshots: has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 - optional: true has-symbols@1.1.0: {} @@ -13251,6 +13378,8 @@ snapshots: is-arrayish@0.3.4: {} + is-callable@1.2.7: {} + is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -13300,6 +13429,10 @@ snapshots: is-stream@2.0.1: {} + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + is-unicode-supported@0.1.0: {} is-url@1.2.4: {} @@ -13310,10 +13443,26 @@ snapshots: isarray@1.0.0: {} + isarray@2.0.5: {} + isbinaryfile@4.0.10: {} isexe@2.0.0: {} + isomorphic-git@1.37.2: + dependencies: + async-lock: 1.4.1 + clean-git-ref: 2.0.1 + crc-32: 1.2.2 + diff3: 0.0.3 + ignore: 5.3.2 + minimisted: 2.0.1 + pako: 1.0.11 + pify: 4.0.1 + readable-stream: 4.7.0 + sha.js: 2.4.12 + simple-get: 4.0.1 + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -14139,6 +14288,10 @@ snapshots: minimist@1.2.8: {} + minimisted@2.0.1: + dependencies: + minimist: 1.2.8 + minipass-collect@1.0.2: dependencies: minipass: 3.3.6 @@ -14506,6 +14659,8 @@ snapshots: pify@2.3.0: {} + pify@4.0.1: {} + pkce-challenge@5.0.1: {} pkg-types@1.3.1: @@ -14527,6 +14682,8 @@ snapshots: path-data-parser: 0.1.0 points-on-curve: 0.2.0 + possible-typed-array-names@1.1.0: {} + postcss-value-parser@4.2.0: {} postcss@8.5.6: @@ -14565,6 +14722,8 @@ snapshots: process-nextick-args@2.0.1: {} + process@0.11.10: {} + progress@2.0.3: {} promise-inflight@1.0.1: {} @@ -14887,6 +15046,14 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + readdirp@4.1.2: {} rechoir@0.8.0: @@ -15175,10 +15342,25 @@ snapshots: server-destroy@1.0.1: {} + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + setimmediate@1.0.5: {} setprototypeof@1.2.0: {} + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -15236,6 +15418,14 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + simple-swizzle@0.2.4: dependencies: is-arrayish: 0.3.4 @@ -15493,6 +15683,12 @@ snapshots: unorm: 1.6.0 optional: true + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + to-data-view@1.1.0: optional: true @@ -15550,6 +15746,12 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + typescript-eslint@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: '@typescript-eslint/eslint-plugin': 8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) @@ -15827,6 +16029,16 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@1.3.1: dependencies: isexe: 2.0.0