Merge pull request #75037 from NousResearch/sec-fixes

fix(sec): patch vulnerable deps + add publication-age floors and npm script allow-list

Co-authored-by: Kingsley Wong <7207924+datanerdie@users.noreply.github.com>
Co-authored-by: viky <vikyw89@gmail.com>
Co-authored-by: FT_IOxCS <237263164+ft-ioxcs@users.noreply.github.com>
Co-authored-by: 方明元 <fmy3@qq.com>
Co-authored-by: Yorkstone Supplies <58149681+sycamoregroupltd@users.noreply.github.com>
Co-authored-by: Steven Cuz Leath <Steven.Leath@gmail.com>
Co-authored-by: Kyle French <248366920+Dadmin88@users.noreply.github.com>
Co-authored-by: Eugeniusz Gilewski <egilewski@egilewski.com>
Co-authored-by: Christopher Gara <79837758+christopherrobin88@users.noreply.github.com>
Co-authored-by: LironTTG <147833337+LironTTG@users.noreply.github.com>
Co-authored-by: Austin Porada <bbasketballer75@gmail.com>
Co-authored-by: cresslank <9219265+cresslank@users.noreply.github.com>
Co-authored-by: Ion Mudreac <mudreac@gmail.com>
Co-authored-by: martinramos002 <262243228+martinramos002-bot@users.noreply.github.com>
Co-authored-by: Sensie-Agents <agents@joinsensie.com>
Co-authored-by: alexwill87 <173086651+alexwill87@users.noreply.github.com>
Co-authored-by: BullishMomentum56 <218643122+BullishMomentum56@users.noreply.github.com>
Co-authored-by: pintadoai <240097310+pintadoai@users.noreply.github.com>
Co-authored-by: Alfred Sahlberg <dinmail@gmail.com>
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
Co-authored-by: Richard Ham <richard.ham@live.com>
Co-authored-by: jrcrittenden <jrcrittenden@gmail.com>
Co-authored-by: 峯岸 亮 <1920071390@campus.ouj.ac.jp>
Co-authored-by: Marcus Martini <6473852+napoleonmm83@users.noreply.github.com>
This commit is contained in:
ethernet 2026-07-31 14:07:32 -04:00 committed by GitHub
commit 6ecd335aa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
84 changed files with 6501 additions and 6909 deletions

47
.npmrc Normal file
View file

@ -0,0 +1,47 @@
# needed to prevent bad npm that has min-release-age but not exclude
engine-strict=true
min-release-age=14
# allow assistant-ui packages & a couple specific deps since they update a LOT.
# remove this when we stabilize (or we haven't updated in 2 wks)
min-release-age-exclude[]=@assistant-ui/*
min-release-age-exclude[]=assistant-cloud
min-release-age-exclude[]=assistant-stream
min-release-age-exclude[]=@radix-ui/*
min-release-age-exclude[]=radix-ui
min-release-age-exclude[]=safe-content-frame
# react-router 8.3.0 includes fixes for vulns. remove this when 8.3.0 is > 2wks old.
min-release-age-exclude[]=react-router
# eslint 10.8.0 includes fixes for vulns. remove this when 10.8.0 is > 2wks old.
min-release-age-exclude[]=eslint
min-release-age-exclude[]=@eslint/*
# tar 7.5.21 includes fixes for vulns. remove this when 7.5.21 is > 2wks old
min-release-age-exclude[]=tar
# concurrently 10.0.4 includes fixes for vulns. remove this when 10.0.4 is > 2wks old
min-release-age-exclude[]=concurrently
# fast-uri 3.1.4 includes fixes for vulns. remove this when 3.1.4 is > 2wks old
min-release-age-exclude[]=fast-uri
# minimatch 10.2.6 includes fixes for vulns. remove this when 10.2.6 is > 2wks old
min-release-age-exclude[]=minimatch
# brace-expansion 5.0.8 includes fixes for vulns. remove this when 5.0.8 is > 2wks old
min-release-age-exclude[]=brace-expansion
# vite 8.2.0 is the first release depending on rolldown >= 1.2.1, which fixes
# a rolldown panic that breaks `npm run build` in apps/desktop
# (rolldown/rolldown#10337 — a regression in 1.1.5, the version vite 8.1.5
# pins as ~1.1.5). @oxc-project/types is here because rolldown 1.2.1 pins it
# as `=0.142.0` — an exact pin, so no older release satisfies it and the age
# gate would fail the whole install with ETARGET.
# remove these once vite 8.2.0 is > 2wks old.
min-release-age-exclude[]=vite
min-release-age-exclude[]=rolldown
min-release-age-exclude[]=@rolldown/*
min-release-age-exclude[]=@oxc-project/types

View file

@ -20,40 +20,32 @@
},
"dependencies": {
"@nous-research/ui": "0.16.0",
"@tailwindcss/vite": "^4.2.4",
"@tailwindcss/typography": "^0.5.19",
"@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.0.0",
"@tauri-apps/plugin-opener": "^2.0.0",
"@tauri-apps/plugin-process": "^2.0.0",
"@tauri-apps/plugin-shell": "^2.0.0",
"@vscode/codicons": "^0.0.45",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"katex": "^0.16.45",
"lucide-react": "^0.577.0",
"nanostores": "^1.3.0",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.1",
"tw-shimmer": "^0.4.11"
"@tailwindcss/typography": "0.5.20",
"@tailwindcss/vite": "4.3.3",
"@tauri-apps/api": "2.11.1",
"@tauri-apps/plugin-dialog": "2.7.1",
"@tauri-apps/plugin-opener": "2.5.4",
"@tauri-apps/plugin-process": "2.3.1",
"@tauri-apps/plugin-shell": "2.3.5",
"@vscode/codicons": "0.0.45",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"katex": "0.16.47",
"lucide-react": "0.577.0",
"nanostores": "1.4.0",
"radix-ui": "1.6.7",
"react": "19.2.7",
"react-dom": "19.2.7",
"tailwind-merge": "3.6.0",
"tailwindcss": "4.3.3",
"tw-shimmer": "0.4.12"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@tauri-apps/cli": "^2.0.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"eslint": "^9.39.4",
"eslint-plugin-perfectionist": "^5.9.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^17.4.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1",
"vite": "^8.0.16"
"@tauri-apps/cli": "2.11.4",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "6.0.3",
"typescript": "6.0.3",
"vite": "8.2.0"
}
}

View file

@ -12,9 +12,9 @@
},
"scripts": {
"clean": "npm run clean:e2e && npm run clean:renderer && npm run clean:electron",
"clean:e2e":"tsc --build tsconfig.e2e.json --clean",
"clean:renderer":"tsc --build tsconfig.json --clean ",
"clean:electron":"tsc --build tsconfig.electron.json --clean",
"clean:e2e": "tsc --build tsconfig.e2e.json --clean",
"clean:renderer": "tsc --build tsconfig.json --clean ",
"clean:electron": "tsc --build tsconfig.electron.json --clean",
"dev": "concurrently -k \"npm:dev:renderer\" \"npm:dev:electron\"",
"dev:fake-boot": "cross-env HERMES_DESKTOP_BOOT_FAKE=1 HERMES_DESKTOP_BOOT_FAKE_STEP_MS=650 npm run dev",
"dev:mock": "node scripts/dev-mock.mjs",
@ -64,110 +64,100 @@
"test:e2e:update-snapshots": "npm run build && WLR_BACKENDS=headless WLR_NO_HARDWARE_CURSORS=1 cage -- npx playwright test e2e/ --reporter=list --update-snapshots"
},
"dependencies": {
"@assistant-ui/react": "^0.14.23",
"@assistant-ui/react-streamdown": "^0.3.4",
"@audiowave/react": "^0.6.2",
"@chenglou/pretext": "^0.0.6",
"@codemirror/commands": "^6.10.4",
"@codemirror/language": "^6.12.4",
"@codemirror/language-data": "^6.5.2",
"@codemirror/state": "^6.7.0",
"@codemirror/view": "^6.43.3",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@assistant-ui/react": "0.15.1",
"@assistant-ui/react-streamdown": "0.3.8",
"@audiowave/react": "0.6.2",
"@chenglou/pretext": "0.0.6",
"@codemirror/commands": "6.10.4",
"@codemirror/language": "6.12.4",
"@codemirror/language-data": "6.5.2",
"@codemirror/state": "6.7.1",
"@codemirror/view": "6.43.6",
"@dnd-kit/core": "6.3.1",
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"@hermes/shared": "file:../shared",
"@icons-pack/react-simple-icons": "=13.11.1",
"@lezer/highlight": "^1.2.3",
"@nanostores/react": "^1.1.0",
"@nous-research/ui": "^0.13.0",
"@radix-ui/react-slot": "^1.2.4",
"@streamdown/code": "^1.1.1",
"@tabler/icons-react": "^3.41.1",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.2.4",
"@tanstack/react-query": "^5.100.6",
"@tanstack/react-virtual": "^3.13.24",
"@vscode/codicons": "^0.0.45",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-serialize": "^0.14.0",
"@xterm/addon-unicode11": "^0.9.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"d3-force": "^3.0.0",
"dnd-core": "^14.0.1",
"dompurify": "^3.4.11",
"emojibase-data": "^16.0.3",
"fflate": "^0.8.3",
"frimousse": "^0.3.0",
"hast-util-from-html-isomorphic": "^2.0.0",
"hast-util-to-text": "^4.0.2",
"ignore": "^7.0.5",
"katex": "^0.16.45",
"mermaid": "^11.15.0",
"motion": "^12.38.0",
"nanostores": "^1.3.0",
"@icons-pack/react-simple-icons": "13.11.1",
"@lezer/highlight": "1.2.3",
"@nanostores/react": "1.1.0",
"@nous-research/ui": "0.16.0",
"@streamdown/code": "1.1.1",
"@tabler/icons-react": "3.44.0",
"@tailwindcss/typography": "0.5.20",
"@tailwindcss/vite": "4.3.3",
"@tanstack/react-query": "5.101.2",
"@tanstack/react-virtual": "3.14.6",
"@vscode/codicons": "0.0.45",
"@xterm/addon-fit": "0.11.0",
"@xterm/addon-serialize": "0.14.0",
"@xterm/addon-unicode11": "0.9.0",
"@xterm/addon-web-links": "0.12.0",
"@xterm/addon-webgl": "0.19.0",
"@xterm/xterm": "6.0.0",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"cmdk": "1.1.1",
"d3-force": "3.0.0",
"dnd-core": "14.0.1",
"dompurify": "3.4.12",
"emojibase-data": "16.0.3",
"fflate": "0.8.3",
"frimousse": "0.3.0",
"hast-util-from-html-isomorphic": "2.0.0",
"hast-util-to-text": "4.0.2",
"ignore": "7.0.6",
"katex": "0.16.47",
"mermaid": "11.16.0",
"motion": "12.42.2",
"nanostores": "1.4.0",
"node-pty": "1.1.0",
"radix-ui": "^1.6.5",
"react": "^19.2.5",
"react-arborist": "^3.5.0",
"react-dnd-html5-backend": "^14.0.3",
"react-dom": "^19.2.5",
"react-router-dom": "^7.17.0",
"react-shiki": "^0.9.3",
"remark-math": "^6.0.0",
"remend": "^1.3.0",
"shiki": "^4.0.2",
"simple-git": "^3.36.0",
"streamdown": "^2.5.0",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.4",
"tw-shimmer": "^0.4.11",
"unicode-animations": "^1.0.3",
"unified": "^11.0.5",
"unist-util-visit-parents": "^6.0.2",
"use-stick-to-bottom": "^1.1.6",
"vfile": "^6.0.3",
"web-haptics": "^0.0.6"
"radix-ui": "1.6.7",
"react": "19.2.7",
"react-arborist": "3.13.2",
"react-dnd-html5-backend": "14.1.0",
"react-dom": "19.2.7",
"react-router": "8.3.0",
"react-shiki": "0.9.3",
"remark-math": "6.0.0",
"remend": "1.3.0",
"shiki": "4.3.1",
"simple-git": "3.36.0",
"streamdown": "2.5.0",
"tailwind-merge": "3.6.0",
"tailwindcss": "4.3.3",
"tw-shimmer": "0.4.12",
"unicode-animations": "1.0.3",
"unified": "11.0.5",
"unist-util-visit-parents": "6.0.2",
"use-stick-to-bottom": "1.1.6",
"vfile": "6.0.3",
"web-haptics": "0.0.6"
},
"devDependencies": {
"@electron/rebuild": "^4.0.6",
"@eslint/js": "^9.39.4",
"@playwright/test": "=1.58.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.3.2",
"@types/d3-force": "^3.0.10",
"@types/hast": "^3.0.4",
"@types/node": "^22.20.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.59.1",
"@typescript-eslint/parser": "^8.59.1",
"@vitejs/plugin-react": "^6.0.1",
"@electron/rebuild": "4.2.0",
"@playwright/test": "1.58.2",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.2",
"@types/d3-force": "3.0.10",
"@types/hast": "3.0.5",
"@types/node": "22.20.1",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "6.0.3",
"bippy": "0.5.43",
"concurrently": "^10.0.3",
"cross-env": "^10.1.0",
"concurrently": "10.0.4",
"cross-env": "10.1.0",
"electron": "40.10.2",
"electron-builder": "^26.8.1",
"esbuild": "^0.28.1",
"eslint": "^9.39.4",
"eslint-plugin-perfectionist": "^5.9.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^16.5.0",
"jsdom": "^29.1.1",
"prettier": "^3.8.3",
"rcedit": "^5.0.2",
"tsx": "^4.22.4",
"typescript": "^6.0.3",
"vite": "^8.0.10",
"vitest": "^4.1.5",
"wait-on": "^9.0.5"
"electron-builder": "26.15.3",
"esbuild": "0.28.1",
"jsdom": "29.1.1",
"prettier": "3.9.5",
"rcedit": "5.0.2",
"tsx": "4.23.1",
"typescript": "6.0.3",
"vite": "8.2.0",
"vitest": "4.1.10",
"wait-on": "9.0.10"
},
"build": {
"electronVersion": "40.10.2",

View file

@ -1,6 +1,6 @@
import type * as React from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { ZoomableImage } from '@/components/chat/zoomable-image'
import { PageLoader } from '@/components/page-loader'

View file

@ -10,15 +10,11 @@ import { type ComposerScope, ComposerScopeProvider, MAIN_COMPOSER_SCOPE } from '
import { useComposerDraft } from './use-composer-draft'
const mockComposerApi = { setText: vi.fn() }
const mockComposerApi = { setText: vi.fn(), getState: () => ({ text: '' }) }
vi.mock('@assistant-ui/react', () => ({
useAui: () => ({ composer: () => mockComposerApi }),
useAuiState: (selector: (state: { composer: { text: string } }) => unknown) => selector({ composer: { text: '' } }),
useComposerRuntime: () => ({
getState: () => ({ text: '' }),
subscribe: () => () => undefined
})
useAui: () => ({ composer: () => mockComposerApi, subscribe: () => () => undefined }),
useAuiState: (selector: (state: { composer: { text: string } }) => unknown) => selector({ composer: { text: '' } })
}))
interface ProbeHarnessProps {

View file

@ -1,4 +1,4 @@
import { useAui, useAuiState, useComposerRuntime } from '@assistant-ui/react'
import { useAui, useAuiState } from '@assistant-ui/react'
import { type RefObject, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { SLASH_COMMAND_RE } from '@/lib/chat-runtime'
@ -58,7 +58,6 @@ export function useComposerDraft({
sessionId
}: UseComposerDraftArgs) {
const aui = useAui()
const composerRuntime = useComposerRuntime()
// Which composer this is on the focus bus + which attachment set it owns.
const { attachments: attachmentScope, target } = useComposerScope()
@ -79,7 +78,7 @@ export function useComposerDraft({
const setComposerText = useCallback(
(value: string) => {
try {
aui.composer().setText(value)
aui.composer.setText(value)
} catch {
// Composer core not bound yet — DOM/draftRef carry the text.
}
@ -272,7 +271,7 @@ export function useComposerDraft({
// eslint-disable-next-line no-restricted-syntax -- legitimate non-atom ref write (see eslint rule comment)
useEffect(() => {
const sync = () => {
const text = composerRuntime.getState().text
const text = aui.composer.getState().text
draftRef.current = text
const editor = editorRef.current
@ -304,13 +303,13 @@ export function useComposerDraft({
}, DRAFT_PERSIST_DEBOUNCE_MS)
}
const unsubscribe = composerRuntime.subscribe(sync)
const unsubscribe = aui.subscribe(sync)
return () => {
unsubscribe()
window.clearTimeout(draftPersistTimerRef.current)
}
}, [composerRuntime, queueEditRef])
}, [aui, queueEditRef])
const insertText = (text: string) => {
const base = draftRef.current

View file

@ -1,5 +1,5 @@
import { cleanup, render, screen } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { I18nProvider } from '@/i18n'

View file

@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react'
import { type ReactNode, useEffect, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { blurComposerInput } from '@/app/chat/composer/focus'
import { AGENTS_ROUTE } from '@/app/routes'

View file

@ -4,7 +4,7 @@ import { useQuery } from '@tanstack/react-query'
import type { ReadableAtom } from 'nanostores'
import type * as React from 'react'
import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocation } from 'react-router'
import type { SubmitTextOptions } from '@/app/session/hooks/use-prompt-actions/utils'
import { Thread } from '@/components/assistant-ui/thread'

View file

@ -3,7 +3,7 @@ import { sortableKeyboardCoordinates } from '@dnd-kit/sortable'
import { useStore } from '@nanostores/react'
import type * as React from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocation } from 'react-router'
import { PlatformAvatar } from '@/app/messaging/platform-icon'
import { Button } from '@/components/ui/button'

View file

@ -20,7 +20,7 @@ import {
import { CSS } from '@dnd-kit/utilities'
import { useStore } from '@nanostores/react'
import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { CodeEditor } from '@/components/chat/code-editor'
import { Button } from '@/components/ui/button'

View file

@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react'
import { useQuery } from '@tanstack/react-query'
import { Dialog as DialogPrimitive } from 'radix-ui'
import { memo, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import {
HUD_HEADING,

View file

@ -9,7 +9,7 @@
import { useStore } from '@nanostores/react'
import { type ComponentProps, lazy, memo, type ReactNode, Suspense, useMemo } from 'react'
import { Navigate, Route, Routes, useParams } from 'react-router-dom'
import { Navigate, Route, Routes, useParams } from 'react-router'
import { ContribBoundary } from '@/contrib/react/boundary'
import { useContributions } from '@/contrib/react/use-contributions'

View file

@ -11,7 +11,7 @@
import { useStore } from '@nanostores/react'
import { useQueryClient } from '@tanstack/react-query'
import { type CSSProperties, lazy, type ReactNode, Suspense, useCallback, useEffect, useMemo, useRef } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router'
import { formatRefValue } from '@/components/assistant-ui/directive-text'
import { BootFailureOverlay } from '@/components/boot-failure-overlay'

View file

@ -1,5 +1,5 @@
import { useEffect, useRef } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { closeActiveTab } from '@/app/chat/close-tab'
import { $terminalTakeover, setTerminalTakeover } from '@/app/right-sidebar/store'

View file

@ -1,5 +1,5 @@
import { useCallback, useMemo } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router'
// Read/write an enum-shaped URL search param (e.g. ?tab=foo). Used to make
// tabbed views survive a refresh. Always navigates with replace so tab clicks

View file

@ -1,4 +1,4 @@
import { useLocation } from 'react-router-dom'
import { useLocation } from 'react-router'
import { appViewForPath, isOverlayView } from '@/app/routes'

View file

@ -1,6 +1,6 @@
// @vitest-environment jsdom
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { MessagingPlatformInfo } from '@/types/hermes'

View file

@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react'
import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { useGatewayRequest } from '@/app/gateway/hooks/use-gateway-request'
import { SETTINGS_ROUTE } from '@/app/routes'

View file

@ -1,7 +1,7 @@
import { useStore } from '@nanostores/react'
import { useEffect, useRef } from 'react'
import { createPortal } from 'react-dom'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { sessionTitle } from '@/lib/chat-runtime'
import { cn } from '@/lib/utils'

View file

@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react'
import { type MutableRefObject, useCallback, useEffect, useRef } from 'react'
import type { NavigateFunction } from 'react-router-dom'
import type { NavigateFunction } from 'react-router'
import { revealTreePane } from '@/components/pane-shell/tree/store'
import { deleteSession, getSessionMessages, setSessionArchived } from '@/hermes'

View file

@ -1,7 +1,7 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import type { ReactNode } from 'react'
import { MemoryRouter } from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {

View file

@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react'
import { useQuery } from '@tanstack/react-query'
import type { ChangeEvent } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { useSearchParams } from 'react-router'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router'
import { codiconIcon } from '@/components/ui/codicon'
import { Tip } from '@/components/ui/tooltip'

View file

@ -1,6 +1,6 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
// Radix Select calls scrollIntoView on its items when the content opens; jsdom

View file

@ -1,8 +1,8 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { cleanup, fireEvent, render as rtlRender, screen, waitFor } from '@testing-library/react'
import type { ReactElement } from 'react'
import { MemoryRouter } from 'react-router-dom'
import type * as ReactRouterDom from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import type * as ReactRouterDom from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { ToolsetConfig } from '@/types/hermes'
@ -11,7 +11,7 @@ import type { ToolsetConfig } from '@/types/hermes'
// needs a router context. The navigate spy asserts the deep-link target.
const navigateSpy = vi.fn()
vi.mock('react-router-dom', async importOriginal => ({
vi.mock('react-router', async importOriginal => ({
...(await importOriginal<typeof ReactRouterDom>()),
useNavigate: () => navigateSpy
}))

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { SETTINGS_ROUTE } from '@/app/routes'
import { Button } from '@/components/ui/button'

View file

@ -1,5 +1,5 @@
import { useEffect } from 'react'
import { useSearchParams } from 'react-router-dom'
import { useSearchParams } from 'react-router'
interface DeepLinkHighlightOptions {
param: string

View file

@ -1,5 +1,5 @@
import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import { StatusbarControls } from '@/app/shell/statusbar-controls'

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router'
import { type CommandCenterSection } from '@/app/command-center'
import {

View file

@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react'
import { type ComponentProps, memo, type ReactNode, useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import {
ContextMenu,

View file

@ -1,5 +1,5 @@
import { cleanup, fireEvent, render, screen, within } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import { StatusbarControls, type StatusbarItem } from '@/app/shell/statusbar-controls'

View file

@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react'
import { type ComponentProps, type MouseEvent, type ReactNode, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router'
import { toggleLayoutEditMode } from '@/components/pane-shell/edit-mode'
import { resetLayoutTree } from '@/components/pane-shell/tree/store'

View file

@ -1,8 +1,8 @@
// @vitest-environment jsdom
import { QueryClientProvider } from '@tanstack/react-query'
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import type * as ReactRouterDom from 'react-router-dom'
import { MemoryRouter } from 'react-router'
import type * as ReactRouterDom from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type * as HermesApi from '@/hermes'
@ -40,7 +40,7 @@ vi.mock('@/store/notifications', () => ({
// so the deep-link target is assertable.
const navigateSpy = vi.fn()
vi.mock('react-router-dom', async importOriginal => ({
vi.mock('react-router', async importOriginal => ({
...(await importOriginal<typeof ReactRouterDom>()),
useNavigate: () => navigateSpy
}))

View file

@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react'
import { useQuery } from '@tanstack/react-query'
import type * as React from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate } from 'react-router'
import { ArchiveSkillConfirmDialog } from '@/app/learning/archive-skill-confirm-dialog'
import { CodeEditor } from '@/components/chat/code-editor'

View file

@ -3,8 +3,8 @@ import {
BranchPickerPrimitive,
ErrorPrimitive,
MessagePrimitive,
useAui,
useAuiState,
useMessageRuntime
} from '@assistant-ui/react'
import { useStore } from '@nanostores/react'
import { type FC, useCallback, useMemo, useState } from 'react'
@ -54,7 +54,7 @@ export const AssistantMessage: FC<{
onDismissError?: (messageId: string) => void
}> = ({ onBranchInNewChat, onDismissError }) => {
const messageId = useAuiState(s => s.message.id)
const messageRuntime = useMessageRuntime()
const messageRuntime = useAui().message
const { t } = useI18n()
// PERF: this component must NOT subscribe to the streaming text. Every

View file

@ -1,4 +1,4 @@
import { useAuiState, useMessageRuntime } from '@assistant-ui/react'
import { useAui, useAuiState } from '@assistant-ui/react'
import { useStore } from '@nanostores/react'
import { type MouseEvent, useCallback } from 'react'
@ -106,7 +106,7 @@ export function useTapbackDoubleClick(
role: ChatMessage['role']
): ((event: MouseEvent<HTMLElement>) => void) | undefined {
const enabled = useStore($reactionsEnabled)
const messageRuntime = useMessageRuntime()
const messageRuntime = useAui().message
const onDoubleClick = useCallback(
(event: MouseEvent<HTMLElement>) => {

View file

@ -165,7 +165,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
const next = `${base}${sep}${value}`
draftRef.current = next
aui.composer().setText(next)
aui.composer.setText(next)
const editor = editorRef.current
@ -230,7 +230,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
if (nextDraft !== draftRef.current) {
draftRef.current = nextDraft
aui.composer().setText(nextDraft)
aui.composer.setText(nextDraft)
}
return nextDraft
@ -338,7 +338,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
const finish = () => {
draftRef.current = composerPlainText(editor)
aui.composer().setText(draftRef.current)
aui.composer.setText(draftRef.current)
requestEditFocus()
starter ? window.setTimeout(refreshTrigger, 0) : closeTrigger()
}
@ -381,7 +381,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
rememberInitialDraft()
const nextDraft = composerPlainText(editor)
draftRef.current = nextDraft
aui.composer().setText(nextDraft)
aui.composer.setText(nextDraft)
requestEditFocus()
return true
@ -581,7 +581,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
// and leave revert as the only way out (#49903 is the same unguarded-core
// hazard on the main composer).
try {
aui.composer().send()
aui.composer.send()
} catch {
setSubmitting(false)
}
@ -624,7 +624,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
// down (a send/cancel raced this timer), cancel() throws "Composer is
// not available" as an uncaught renderer error. Nothing to cancel then.
try {
aui.composer().cancel()
aui.composer.cancel()
} catch {
// Composer core already gone — the edit is closing anyway.
}
@ -690,7 +690,7 @@ export const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sess
if (event.key === 'Escape') {
event.preventDefault()
aui.composer().cancel()
aui.composer.cancel()
return
}

View file

@ -1,5 +1,5 @@
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { MemoryRouter, useNavigate } from 'react-router-dom'
import { MemoryRouter, useNavigate } from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { FindBar } from '@/components/find-bar'

View file

@ -1,6 +1,6 @@
import { useStore } from '@nanostores/react'
import { useEffect, useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocation } from 'react-router'
import { Tip } from '@/components/ui/tooltip'
import { useI18n } from '@/i18n'

View file

@ -16,7 +16,7 @@ import '@/debug/dev-only'
import { QueryClientProvider } from '@tanstack/react-query'
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { HashRouter } from 'react-router-dom'
import { HashRouter } from 'react-router'
import App from './app'
import { ErrorBoundary } from './components/error-boundary'

View file

@ -19,10 +19,6 @@
"check": "npm run typecheck && npm run lint"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"eslint": "^9.39.4",
"globals": "^17.4.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1"
"typescript": "6.0.3"
}
}

View file

@ -0,0 +1,2 @@
Leathal1
# PR #69711 salvage (deps: pyasn1 0.6.4)

View file

@ -0,0 +1,2 @@
Sensie-agents
# PR #65150 salvage (deps: lazy dependency floors)

View file

@ -0,0 +1,2 @@
bbasketballer75
# PR #69864, #73857 salvage (deps: npm advisories)

View file

@ -0,0 +1,2 @@
sahlbergalfred4-lgtm
# PR #70003 salvage (deps: security-sensitive packages)

View file

@ -0,0 +1,2 @@
superafun
# PR #60201 salvage (sec: npm allowScripts allowlist)

View file

@ -0,0 +1,2 @@
jrcrittenden
# PR #28749 salvage (sec: release-age gate on hermes update)

View file

@ -0,0 +1,2 @@
mudrii
# PR #66871, #63099 salvage (deps: vulnerable floors)

View file

@ -0,0 +1,2 @@
zebadee2kk
# PR #50052 salvage (sec: bump deps for 25 CVEs)

View file

@ -0,0 +1,2 @@
vikyw89
# PR #50902 salvage (sec: npm/uv dependency cooldown)

View file

@ -12,10 +12,8 @@
*/
import js from '@eslint/js'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import typescriptParser from '@typescript-eslint/parser'
import tseslint from 'typescript-eslint';
import perfectionist from 'eslint-plugin-perfectionist'
import reactPlugin from 'eslint-plugin-react'
import hooksPlugin from 'eslint-plugin-react-hooks'
import unusedImports from 'eslint-plugin-unused-imports'
import globals from 'globals'
@ -31,7 +29,7 @@ export default [
globals: {
...globals.node
},
parser: typescriptParser,
parser: tseslint.parser,
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
@ -39,9 +37,8 @@ export default [
}
},
plugins: {
'@typescript-eslint': typescriptEslint,
'@typescript-eslint': tseslint.plugin,
perfectionist,
react: reactPlugin,
'react-hooks': hooksPlugin,
'unused-imports': unusedImports
},
@ -95,9 +92,6 @@ export default [
'react-hooks/exhaustive-deps': 'warn',
'react-hooks/rules-of-hooks': 'error',
'unused-imports/no-unused-imports': 'error'
},
settings: {
react: { version: 'detect' }
}
},
{

38
flake.lock generated
View file

@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"lastModified": 1782949081,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"type": "github"
},
"original": {
@ -22,11 +22,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1775036866,
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
"lastModified": 1785318670,
"narHash": "sha256-dN6Ou5x/+23FZLEpYP3IffO+NyJFzUlGumt1uu3MMaY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
"rev": "0954f7ee2f6bb3dc7d4e3d0d8bcb8fd4bde4cfc5",
"type": "github"
},
"original": {
@ -69,11 +69,11 @@
]
},
"locked": {
"lastModified": 1772555609,
"narHash": "sha256-3BA3HnUvJSbHJAlJj6XSy0Jmu7RyP2gyB/0fL7XuEDo=",
"lastModified": 1785115949,
"narHash": "sha256-8AM37BfyGaL2v/SZyg4PupRxJ01Y4htvM+WrTjWrPpo=",
"owner": "pyproject-nix",
"repo": "build-system-pkgs",
"rev": "c37f66a953535c394244888598947679af231863",
"rev": "62c0d86027edb1c4f39a5facc09876348144f7c9",
"type": "github"
},
"original": {
@ -82,18 +82,18 @@
"type": "github"
}
},
"pyproject-nix_2": {
"pyproject-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772865871,
"narHash": "sha256-/ZTSg97aouL0SlPHaokA4r3iuH9QzHVuWPACD2CUCFY=",
"lastModified": 1784591072,
"narHash": "sha256-zP/WaDxrRu8GANZM61+V2LT/7ycEEdoyLWn7M6WzU7M=",
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"rev": "e537db02e72d553cea470976b9733581bcf5b3ed",
"rev": "e3b599ca2e7fcf93d4edf65d7f19bbf6491724f3",
"type": "github"
},
"original": {
@ -108,11 +108,11 @@
"nixpkgs": "nixpkgs",
"npm-lockfile-fix": "npm-lockfile-fix",
"pyproject-build-systems": "pyproject-build-systems",
"pyproject-nix": "pyproject-nix_2",
"uv2nix": "uv2nix_2"
"pyproject-nix": "pyproject-nix",
"uv2nix": "uv2nix"
}
},
"uv2nix_2": {
"uv2nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@ -122,11 +122,11 @@
]
},
"locked": {
"lastModified": 1773039484,
"narHash": "sha256-+boo33KYkJDw9KItpeEXXv8+65f7hHv/earxpcyzQ0I=",
"lastModified": 1785277507,
"narHash": "sha256-9Tq3UDX2hD/aveW/HvkBlAmEwJTOlY5HQXJM+L5BGmE=",
"owner": "pyproject-nix",
"repo": "uv2nix",
"rev": "b68be7cfeacbed9a3fa38a2b5adc0cfb81d9bb1f",
"rev": "5a836d395cbf5fc22670eb98dd4aa4fc4d406977",
"type": "github"
},
"original": {

View file

@ -2023,30 +2023,44 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
tui_dir,
include_child_workspaces=True,
)
result = subprocess.run(
[
npm,
"install",
*npm_workspace_args,
# --include=dev: ui-tui's build toolchain (esbuild, typescript)
# lives in devDependencies. An inherited NODE_ENV=production
# (e.g. from a container shell or a parent TUI launch) or an
# npm `omit=dev` config would silently skip them and the TUI
# build would fail. See _run_npm_install_deterministic.
"--include=dev",
"--silent",
"--no-fund",
"--no-audit",
"--progress=false",
],
cwd=str(npm_cwd),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace",
env={**os.environ, "CI": "1"},
)
npm_install_cmd = [
npm,
"install",
*npm_workspace_args,
# --include=dev: ui-tui's build toolchain (esbuild, typescript)
# lives in devDependencies. An inherited NODE_ENV=production
# (e.g. from a container shell or a parent TUI launch) or an
# npm `omit=dev` config would silently skip them and the TUI
# build would fail. See _run_npm_install_deterministic.
"--include=dev",
"--silent",
"--no-fund",
"--no-audit",
"--progress=false",
]
def _run_tui_install() -> subprocess.CompletedProcess:
return subprocess.run(
npm_install_cmd,
cwd=str(npm_cwd),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace",
env={**os.environ, "CI": "1"},
)
result = _run_tui_install()
if result.returncode != 0:
# An npm outside the root package.json's `engines.npm` range fails
# here before doing any work; upgrade a Hermes-managed npm once and
# retry rather than dumping EBADENGINE at the user.
from hermes_cli.npm_engine import maybe_repair_npm_engine
combined_output = f"{result.stdout or ''}\n{result.stderr or ''}"
if maybe_repair_npm_engine(npm, combined_output):
result = _run_tui_install()
if result.returncode != 0:
combined = f"{result.stdout or ''}\n{result.stderr or ''}".strip()
preview = "\n".join(combined.splitlines()[-30:])
@ -5454,34 +5468,84 @@ def _run_npm_install_deterministic(
# install path and nix/lib.nix npm ci hooks.
run_env = {**os.environ, **(env or {}), "CI": "1"}
lockfile = cwd / "package-lock.json"
if lockfile.exists():
ci_cmd = [npm, "ci", "--include=dev", *extra_args]
ci_result = subprocess.run(
ci_cmd,
def _run(cmd: list[str]) -> subprocess.CompletedProcess:
return _run_npm_watching_for_engine_failure(
cmd,
cwd=cwd,
env=run_env,
capture_output=capture_output,
)
def _attempt() -> subprocess.CompletedProcess:
lockfile = cwd / "package-lock.json"
if lockfile.exists():
ci_result = _run([npm, "ci", "--include=dev", *extra_args])
if ci_result.returncode == 0:
return ci_result
# Fall through to `npm install` — lockfile may be out of sync on a
# WIP fork/branch, or `npm ci` may not be available on very old npm.
return _run([npm, "install", "--no-save", "--include=dev", *extra_args])
result = _attempt()
if result.returncode == 0:
return result
# An npm outside the root package.json's `engines.npm` range fails every
# command here identically (the `npm install` fallback included), so the
# failure is worth exactly one upgrade attempt. `maybe_repair_npm_engine`
# returns True only when it actually upgraded a Hermes-managed npm.
from hermes_cli.npm_engine import maybe_repair_npm_engine
combined = f"{result.stdout or ''}\n{result.stderr or ''}"
if not maybe_repair_npm_engine(npm, combined):
return result
return _attempt()
def _run_npm_watching_for_engine_failure(
cmd: list[str],
*,
cwd: Path,
env: dict[str, str],
capture_output: bool,
) -> subprocess.CompletedProcess:
"""Run *cmd*, always retaining stderr so ``EBADENGINE`` stays detectable.
``capture_output=False`` callers stream npm's progress live and would
otherwise hand back a ``CompletedProcess`` with ``stderr=None``, leaving the
engine-failure recovery nothing to read. Tee stderr instead: each line is
forwarded to this process's stderr as it arrives (so live output is
unchanged) and accumulated for the caller.
"""
if capture_output:
return subprocess.run(
cmd,
cwd=cwd,
env=env,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
check=False,
)
if ci_result.returncode == 0:
return ci_result
# Fall through to `npm install` — lockfile may be out of sync on a
# WIP fork/branch, or `npm ci` may not be available on very old npm.
install_cmd = [npm, "install", "--no-save", "--include=dev", *extra_args]
return subprocess.run(
install_cmd,
captured: list[str] = []
with subprocess.Popen(
cmd,
cwd=cwd,
env=run_env,
capture_output=capture_output,
env=env,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace",
check=False,
)
) as proc:
if proc.stderr is not None:
for line in proc.stderr:
captured.append(line)
sys.stderr.write(line)
sys.stderr.flush()
returncode = proc.wait()
return subprocess.CompletedProcess(cmd, returncode, None, "".join(captured))
def _missing_web_build_tool(output: str) -> str | None:

276
hermes_cli/npm_engine.py Normal file
View file

@ -0,0 +1,276 @@
"""Recover from npm ``EBADENGINE`` failures by upgrading a managed npm.
The repo's ``.npmrc`` sets ``engine-strict=true`` and the root ``package.json``
pins an ``engines.npm`` range, so an npm outside that range aborts every
``npm ci`` / ``npm install`` we run inside the checkout::
npm error code EBADENGINE
npm error notsup Required: {"node":">=20.0.0","npm":"<11.10.0 || >=12.0.0"}
npm error notsup Actual: {"npm":"10.9.8","node":"v22.23.1"}
Rather than predicting the failure (which would mean a semver range matcher and
an ``npm --version`` probe before work that usually succeeds), we react to it:
npm states the required range in the error, so the recovery reads the
constraint straight out of the output it just produced.
Scope of the repair is deliberately narrow. Hermes only upgrades an npm that
lives inside its **own** managed Node tree (``$HERMES_HOME/node``), installing
in place with ``--prefix`` so ``bin/npm`` keeps resolving to the upgraded
``lib/node_modules/npm``. A system / nvm / brew / Nix npm belongs to the user
and their other projects; for those we print the exact command and let the
original failure stand.
"""
from __future__ import annotations
import json
import os
import re
import subprocess
import sys
import tempfile
from pathlib import Path
from hermes_constants import get_hermes_home, with_hermes_node_path
__all__ = [
"is_ebadengine",
"required_npm_range",
"managed_npm_prefix",
"upgrade_managed_npm",
"maybe_repair_npm_engine",
]
# npm prints `npm error notsup Required: {...}` on npm >= 10 and
# `npm ERR! notsup Required: {...}` on older releases.
_REQUIRED_RE = re.compile(r"Required:\s*(\{.*?\})")
_ACTUAL_RE = re.compile(r"Actual:\s*(\{.*?\})")
# Wall-clock cap for the self-upgrade. The measured in-place upgrade of a
# managed tree takes ~1s; this only has to cover a slow registry.
_UPGRADE_TIMEOUT = 300
def is_ebadengine(output: str) -> bool:
"""Return True when *output* is an npm engine-compatibility failure."""
if not output:
return False
return "EBADENGINE" in output or "Unsupported engine" in output
def _iter_required_blocks(output: str) -> list[dict]:
blocks: list[dict] = []
for match in _REQUIRED_RE.finditer(output or ""):
try:
parsed = json.loads(match.group(1))
except ValueError:
continue
if isinstance(parsed, dict):
blocks.append(parsed)
return blocks
def required_npm_range(output: str) -> str | None:
"""Return the ``engines.npm`` range npm demanded in *output*.
Returns ``None`` when the output has no engine failure, or when the
failure is about Node rather than npm upgrading npm cannot fix a Node
version mismatch, so the caller must not try.
When several packages report conflicting npm ranges the repo's own root
constraint is preferred (it is the one we control); otherwise the first
range wins, since any of them is a strict improvement over an npm that
satisfies none.
"""
if not is_ebadengine(output):
return None
ranges = [
str(block["npm"]).strip()
for block in _iter_required_blocks(output)
if block.get("npm")
]
if not ranges:
return None
distinct = list(dict.fromkeys(ranges))
if len(distinct) > 1:
repo_range = _repo_npm_range()
if repo_range in distinct:
return repo_range
return distinct[0]
def actual_npm_version(output: str) -> str | None:
"""Return the npm version npm reported as ``Actual`` in *output*."""
for match in _ACTUAL_RE.finditer(output or ""):
try:
parsed = json.loads(match.group(1))
except ValueError:
continue
if isinstance(parsed, dict) and parsed.get("npm"):
return str(parsed["npm"]).strip()
return None
def _repo_npm_range() -> str | None:
"""Return ``engines.npm`` from the checkout's root ``package.json``."""
package_json = Path(__file__).resolve().parent.parent / "package.json"
try:
data = json.loads(package_json.read_text(encoding="utf-8"))
except (OSError, ValueError):
return None
engines = data.get("engines")
if not isinstance(engines, dict):
return None
value = engines.get("npm")
return str(value).strip() if value else None
def managed_npm_prefix(npm: str | os.PathLike[str] | None) -> Path | None:
"""Return the Hermes-managed Node root *npm* lives in, else ``None``.
Symlinks are resolved first: an install links ``~/.local/bin/npm`` at
``$HERMES_HOME/node/bin/npm``, which itself links into
``lib/node_modules/npm/bin/npm-cli.js``. Every one of those spellings is
the managed npm and must be recognised as such, or the repair silently
declines to fix the very install it owns.
"""
if not npm:
return None
prefix = get_hermes_home() / "node"
try:
resolved = Path(npm).resolve()
prefix_resolved = prefix.resolve()
except OSError:
return None
if resolved == prefix_resolved or prefix_resolved in resolved.parents:
return prefix
return None
def _upgrade_env() -> dict[str, str]:
env = with_hermes_node_path()
# The checkout's .npmrc sets `min-release-age`, which would gate the npm
# release we are trying to install. The upgrade runs from a temp cwd so
# that file is out of scope; this neutralises a user-level ~/.npmrc too.
env["npm_config_min_release_age"] = "0"
# `unicode-animations`-style postinstall animations no-op under CI=1.
env["CI"] = "1"
return env
def upgrade_managed_npm(
npm: str,
npm_range: str,
*,
prefix: Path,
quiet: bool = False,
) -> bool:
"""Upgrade the managed npm at *npm* in place to satisfy *npm_range*.
``--prefix`` targets the managed tree explicitly: a managed install writes
``prefix=~/.local`` into ``$HERMES_HOME/node/etc/npmrc`` so that global
installs land on PATH, and without the override the "upgrade" would install
a second npm somewhere else while the managed one stayed stale.
"""
if not quiet:
print(
f"→ Upgrading Hermes-managed npm to satisfy {npm_range}",
flush=True,
)
try:
# A temp cwd keeps the checkout's .npmrc (engine-strict, min-release-age)
# from applying to the upgrade itself.
with tempfile.TemporaryDirectory(prefix="hermes-npm-upgrade-") as tmp:
result = subprocess.run(
[
npm,
"install",
"--global",
"--prefix",
str(prefix),
f"npm@{npm_range}",
"--no-fund",
"--no-audit",
"--progress=false",
],
cwd=tmp,
env=_upgrade_env(),
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
timeout=_UPGRADE_TIMEOUT,
check=False,
)
except (OSError, subprocess.SubprocessError):
if not quiet:
print(" ✗ npm upgrade could not be started", file=sys.stderr)
return False
if result.returncode != 0:
if not quiet:
detail = (result.stderr or result.stdout or "").strip().splitlines()
print(" ✗ npm upgrade failed", file=sys.stderr)
for line in detail[-10:]:
print(f" {line}", file=sys.stderr)
return False
if not quiet:
print(f" ✓ npm upgraded to {_probe_version(npm) or npm_range}", flush=True)
return True
def _probe_version(npm: str) -> str | None:
try:
result = subprocess.run(
[npm, "--version"],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
timeout=30,
env=with_hermes_node_path(),
check=False,
)
except (OSError, subprocess.SubprocessError):
return None
return (result.stdout or "").strip() or None
def _print_manual_fix(npm: str, npm_range: str, actual: str | None) -> None:
have = f"npm {actual} " if actual else "This npm "
print(
f"\n{have}does not satisfy the range this project requires: {npm_range}\n"
f" Resolved npm: {npm}\n"
" Hermes only upgrades npm inside its own managed Node install, so this\n"
" one is left alone. Upgrade it yourself with:\n"
f' npm install -g npm@"{npm_range}"',
file=sys.stderr,
)
def maybe_repair_npm_engine(
npm: str | None,
output: str,
*,
quiet: bool = False,
) -> bool:
"""Repair an ``EBADENGINE`` failure when Hermes owns the npm involved.
*output* is the combined stdout/stderr of the npm command that just failed.
Returns ``True`` only when npm was actually upgraded, meaning the caller
should retry its command once. Returns ``False`` for every other case
not an engine failure, a Node (not npm) mismatch, an npm Hermes does not
own, or a failed upgrade leaving the original failure to stand.
"""
npm_range = required_npm_range(output)
if not npm_range or not npm:
return False
prefix = managed_npm_prefix(npm)
if prefix is None:
if not quiet:
_print_manual_fix(npm, npm_range, actual_npm_version(output))
return False
return upgrade_managed_npm(npm, npm_range, prefix=prefix, quiet=quiet)

View file

@ -10,7 +10,7 @@
makeWrapper,
callPackage,
python312,
nodejs_22,
nodejs_26,
electron,
ripgrep,
git,
@ -39,7 +39,7 @@
extraDependencyGroups ? [ ],
}:
let
nodejs = nodejs_22;
nodejs = nodejs_26;
mkHermesVenv =
extraDependencyGroups:
callPackage ./python.nix {

8149
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -35,15 +35,35 @@
},
"homepage": "https://github.com/NousResearch/Hermes-Agent#readme",
"dependencies": {
"@streamdown/math": "^1.0.2",
"agent-browser": "^0.26.0"
"@streamdown/math": "1.0.2",
"agent-browser": "0.26.0"
},
"devDependencies": {
"@eslint/js": "9.39.5",
"typescript-eslint": "8.64.0",
"eslint-plugin-perfectionist": "5.10.0",
"eslint-plugin-react-hooks": "7.1.1",
"eslint-plugin-unused-imports": "4.4.1",
"globals": "17.7.0"
},
"overrides": {
"lodash": "4.18.1",
"yauzl": "^3.3.1",
"protobufjs": "^8.7.1"
"protobufjs": "^8.7.1",
"brace-expansion": "5.0.8"
},
"engines": {
"node": ">=20.0.0"
"node": ">=20.0.0",
"npm": "<11.10.0 || >=12.0.0"
},
"allowScripts": {
"unicode-animations": false,
"esbuild@0.28.1": true,
"node-pty@1.1.0": true,
"electron-winstaller@5.4.0": true,
"agent-browser@0.26.0": true,
"electron@40.10.2": true,
"fsevents@2.3.2": true,
"fsevents@2.3.3": true
}
}

View file

@ -122,7 +122,7 @@ dependencies = [
# libs required for the codecs we use, so it's safe to ship in the base
# install rather than gating it behind an extra + a mid-session lazy install
# (which deadlocked the CLI under prompt_toolkit — see #40490).
"Pillow==12.2.0",
"Pillow==12.3.0",
# Windows log rotation. Stdlib ``RotatingFileHandler.doRollover()`` uses
# ``os.rename()`` which fails with ``PermissionError [WinError 32]`` on
# Windows whenever any other process holds an append-mode handle on
@ -162,7 +162,7 @@ modal = ["modal==1.3.4"]
daytona = ["daytona==0.155.0"]
vercel = ["vercel==0.7.2"]
hindsight = ["hindsight-client==0.6.1"]
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "mcp==1.26.0", "starlette==1.3.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==81.0.0"] # starlette: CVE-2026-48710; setuptools: latest <82 (torch >=2.11 caps setuptools<82)
dev = ["debugpy==1.8.20", "pytest==9.1.1", "pytest-asyncio==1.3.0", "mcp==1.28.1", "starlette==1.3.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==83.0.0"] # starlette: CVE-2026-48710; setuptools: 83 (torch >=2.13 requires setuptools 83)
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.14.1", "brotlicffi==1.2.0.1", "slack-bolt==1.29.0", "slack-sdk==3.43.0", "qrcode==7.4.2"] # aiohttp 3.14.1: CVE-2026-34513/34518/34519/34520/34525 + 34993(RCE)/47265
cron = [] # croniter is now a core dependency; this extra kept for back-compat
slack = ["slack-bolt==1.29.0", "slack-sdk==3.43.0", "aiohttp==3.14.1"]
@ -227,7 +227,7 @@ pty = []
# `request.url` can be bypassed. We pin a patched Starlette directly in every
# extra that exposes a Starlette-backed server surface so pip/uv can't resolve
# a vulnerable pre-1.0.1 transitive. Bump in lockstep with uv.lock.
mcp = ["mcp==1.26.0", "starlette==1.3.1"] # starlette: CVE-2026-48710
mcp = ["mcp==1.28.1", "starlette==1.3.1"] # starlette: CVE-2026-48710
# Backwards-compatible no-op alias. Relay is a core dependency on supported
# wheel targets and intentionally unavailable on other platforms.
nemo-relay = []
@ -238,7 +238,7 @@ teams = ["microsoft-teams-apps==2.0.13.4", "aiohttp==3.14.1"] # aiohttp 3.14.1:
# The cua-driver binary itself is installed via `hermes tools` post-setup
# (curl install script); this extra just pins the MCP client used to talk
# to it, which is already provided by the `mcp` extra.
computer-use = ["mcp==1.26.0", "starlette==1.3.1"] # starlette: CVE-2026-48710
computer-use = ["mcp==1.28.1", "starlette==1.3.1"] # starlette: CVE-2026-48710
acp = ["agent-client-protocol==0.9.0"]
# mistral: Voxtral STT + TTS. Pinned to an exact verified-clean version.
# The `mistralai` PyPI project was quarantined 2026-05-12 after the malicious
@ -335,7 +335,7 @@ all = [
]
[build-system]
requires = ["setuptools>=77.0,<83"]
requires = ["setuptools==83.0.0"]
build-backend = "setuptools.build_meta"
[project.scripts]
@ -343,6 +343,17 @@ hermes = "hermes_cli.main:main"
hermes-agent = "run_agent:main"
hermes-acp = "acp_adapter.entry:main"
[tool.uv]
override-dependencies = [
# discord.py's latest published version, 2.7.1, pins pynacl at <1.6. however, pynacl 1.5.0 has known vulnerabilities.
# discord.py has updated pynacl to 1.6 on `main`, but has not yet published a patch release.
# so, we force-override this to pynacl 1.6. here.
# remove this when we update discord.py
"pynacl>=1.6,<1.7",
]
exclude-newer = "14 days"
exclude-newer-package = { vercel = false, nemo-relay = false, huggingface_hub = false }
[tool.setuptools]
# Top-level single-file modules (not packages). Without this, uv2nix's
# sealed venv is missing hermes_constants, run_agent, etc.

View file

@ -11,10 +11,9 @@
"fix": "npm run lint:fix"
},
"devDependencies": {
"eslint": "^9.39.4",
"@types/plist": "^3.0.5",
"plist": "^3.1.0",
"typescript": "^6.0.3",
"vitest": "^4.1.9"
"@types/plist": "3.0.5",
"plist": "3.1.1",
"typescript": "6.0.3",
"vitest": "4.1.10"
}
}

View file

@ -0,0 +1,229 @@
"""Tests for npm ``EBADENGINE`` recovery (``hermes_cli/npm_engine.py``).
The behaviour under test is a contract about *reacting* to npm's own engine
check: npm states the range it wants in the failure, Hermes upgrades only an
npm it owns, and every other case leaves the original failure alone.
"""
import json
import subprocess
from pathlib import Path
import pytest
from hermes_cli.npm_engine import (
actual_npm_version,
is_ebadengine,
managed_npm_prefix,
maybe_repair_npm_engine,
required_npm_range,
)
# Verbatim npm 10 output shape (`npm error`), and the npm 9 shape (`npm ERR!`).
EBADENGINE_OUTPUT = """
npm error code EBADENGINE
npm error engine Unsupported engine
npm error engine Not compatible with your version of node/npm: hermes-agent@1.0.0
npm error notsup Not compatible with your version of node/npm: hermes-agent@1.0.0
npm error notsup Required: {"node":">=20.0.0","npm":"<11.10.0 || >=12.0.0"}
npm error notsup Actual: {"npm":"11.10.0","node":"v22.23.1"}
"""
LEGACY_EBADENGINE_OUTPUT = """
npm ERR! code EBADENGINE
npm ERR! engine Unsupported engine
npm ERR! notsup Required: {"node":">=20.0.0","npm":">=12.0.0"}
npm ERR! notsup Actual: {"npm":"9.6.7","node":"v20.1.0"}
"""
# A lockfile mismatch — the other common `npm ci` failure. Must NOT be treated
# as an engine problem, or every out-of-sync lockfile would trigger an upgrade.
ELOCK_OUTPUT = """
npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json and
npm error package-lock.json are in sync.
"""
class TestDetection:
def test_recognises_modern_and_legacy_engine_failures(self):
assert is_ebadengine(EBADENGINE_OUTPUT)
assert is_ebadengine(LEGACY_EBADENGINE_OUTPUT)
def test_unrelated_failures_are_not_engine_failures(self):
assert not is_ebadengine(ELOCK_OUTPUT)
assert not is_ebadengine("")
assert not is_ebadengine("npm error code E404")
def test_range_comes_from_the_error_not_a_hardcoded_list(self):
assert required_npm_range(EBADENGINE_OUTPUT) == "<11.10.0 || >=12.0.0"
assert required_npm_range(LEGACY_EBADENGINE_OUTPUT) == ">=12.0.0"
def test_actual_version_is_reported_back(self):
assert actual_npm_version(EBADENGINE_OUTPUT) == "11.10.0"
def test_no_range_for_non_engine_output(self):
assert required_npm_range(ELOCK_OUTPUT) is None
assert required_npm_range("") is None
def test_node_only_mismatch_yields_no_npm_range(self):
"""Upgrading npm cannot fix a Node version mismatch, so don't try."""
node_only = (
'npm error code EBADENGINE\n'
'npm error notsup Required: {"node":">=20.0.0"}\n'
'npm error notsup Actual: {"npm":"10.9.8","node":"v18.0.0"}\n'
)
assert required_npm_range(node_only) is None
def test_malformed_required_block_is_ignored(self):
broken = (
"npm error code EBADENGINE\n"
"npm error notsup Required: {not json}\n"
)
assert required_npm_range(broken) is None
class TestManagedDetection:
"""The upgrade must fire for every spelling of the managed npm, and for
no other npm this is the boundary between "Hermes fixes it" and "the
user's own toolchain is left alone"."""
@pytest.fixture
def managed_tree(self, tmp_path, monkeypatch):
home = tmp_path / ".hermes"
node = home / "node"
(node / "bin").mkdir(parents=True)
(node / "lib" / "node_modules" / "npm" / "bin").mkdir(parents=True)
cli = node / "lib" / "node_modules" / "npm" / "bin" / "npm-cli.js"
cli.write_text("#!/usr/bin/env node\n", encoding="utf-8")
(node / "bin" / "npm").symlink_to(cli)
monkeypatch.setenv("HERMES_HOME", str(home))
return home
def test_direct_managed_bin_is_managed(self, managed_tree):
npm = managed_tree / "node" / "bin" / "npm"
assert managed_npm_prefix(npm) == managed_tree / "node"
def test_symlink_from_local_bin_resolves_to_managed(self, managed_tree, tmp_path):
"""An install links ~/.local/bin/npm at the managed tree; that link is
the npm a user's PATH actually resolves, so it must count as managed."""
local_bin = tmp_path / "local-bin"
local_bin.mkdir()
link = local_bin / "npm"
link.symlink_to(managed_tree / "node" / "bin" / "npm")
assert managed_npm_prefix(link) == managed_tree / "node"
def test_system_npm_is_not_managed(self, managed_tree, tmp_path):
system_npm = tmp_path / "usr" / "bin" / "npm"
system_npm.parent.mkdir(parents=True)
system_npm.write_text("#!/bin/sh\n", encoding="utf-8")
assert managed_npm_prefix(system_npm) is None
def test_no_npm_is_not_managed(self, managed_tree):
assert managed_npm_prefix(None) is None
assert managed_npm_prefix("") is None
class TestRepairDecision:
"""`maybe_repair_npm_engine` returns True only when it actually upgraded,
because its return value is what gates the caller's single retry."""
@pytest.fixture
def managed_npm(self, tmp_path, monkeypatch):
home = tmp_path / ".hermes"
bin_dir = home / "node" / "bin"
bin_dir.mkdir(parents=True)
npm = bin_dir / "npm"
npm.write_text("#!/bin/sh\n", encoding="utf-8")
npm.chmod(0o755)
monkeypatch.setenv("HERMES_HOME", str(home))
return npm
def test_upgrades_managed_npm_with_the_range_npm_asked_for(
self, managed_npm, monkeypatch
):
calls = []
def fake_run(cmd, **kwargs):
calls.append((cmd, kwargs))
return subprocess.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr(subprocess, "run", fake_run)
assert maybe_repair_npm_engine(str(managed_npm), EBADENGINE_OUTPUT, quiet=True)
upgrade_cmd = calls[0][0]
assert upgrade_cmd[1:3] == ["install", "--global"]
# The range must come from npm's error, and target the managed prefix
# explicitly (the managed etc/npmrc points `prefix` elsewhere).
assert "npm@<11.10.0 || >=12.0.0" in upgrade_cmd
prefix_index = upgrade_cmd.index("--prefix")
assert Path(upgrade_cmd[prefix_index + 1]) == managed_npm.parent.parent
def test_upgrade_runs_outside_the_checkout(self, managed_npm, monkeypatch):
"""The repo .npmrc sets min-release-age, which would gate the very npm
release we need; the upgrade must not run under it."""
seen = {}
def fake_run(cmd, **kwargs):
seen.update(kwargs)
return subprocess.CompletedProcess(cmd, 0, "", "")
monkeypatch.setattr(subprocess, "run", fake_run)
maybe_repair_npm_engine(str(managed_npm), EBADENGINE_OUTPUT, quiet=True)
cwd = Path(seen["cwd"])
assert not (cwd / ".npmrc").exists()
assert seen["env"]["npm_config_min_release_age"] == "0"
def test_failed_upgrade_reports_no_retry(self, managed_npm, monkeypatch):
monkeypatch.setattr(
subprocess,
"run",
lambda cmd, **kw: subprocess.CompletedProcess(cmd, 1, "", "boom"),
)
assert not maybe_repair_npm_engine(
str(managed_npm), EBADENGINE_OUTPUT, quiet=True
)
def test_unmanaged_npm_is_never_touched(self, managed_npm, tmp_path, monkeypatch, capsys):
system_npm = tmp_path / "usr-bin-npm"
system_npm.write_text("#!/bin/sh\n", encoding="utf-8")
def explode(cmd, **kwargs): # pragma: no cover - must not be reached
raise AssertionError(f"must not run a subprocess for a foreign npm: {cmd}")
monkeypatch.setattr(subprocess, "run", explode)
assert not maybe_repair_npm_engine(str(system_npm), EBADENGINE_OUTPUT)
# The user gets the exact command to run, since we refuse to run it.
err = capsys.readouterr().err
assert 'npm install -g npm@"<11.10.0 || >=12.0.0"' in err
def test_non_engine_failure_never_upgrades(self, managed_npm, monkeypatch):
def explode(cmd, **kwargs): # pragma: no cover - must not be reached
raise AssertionError("a lockfile mismatch must not trigger an upgrade")
monkeypatch.setattr(subprocess, "run", explode)
assert not maybe_repair_npm_engine(str(managed_npm), ELOCK_OUTPUT, quiet=True)
class TestRepoRangeIsSatisfiable:
"""Invariant: whatever the root package.json demands, the recovery can
parse and act on it a malformed range would make the repair a no-op."""
def test_root_engines_npm_range_is_a_usable_constraint(self):
repo_root = Path(__file__).resolve().parents[2]
package_json = repo_root / "package.json"
engines = json.loads(package_json.read_text(encoding="utf-8")).get("engines", {})
npm_range = engines.get("npm")
if not npm_range:
pytest.skip("root package.json does not pin engines.npm")
synthetic = (
"npm error code EBADENGINE\n"
'npm error notsup Required: '
+ json.dumps({"node": ">=20.0.0", "npm": npm_range})
+ "\n"
)
assert required_npm_range(synthetic) == npm_range

View file

@ -782,7 +782,7 @@ class TestLazyMcpInstall:
from tools.computer_use import cua_backend
from tools.lazy_deps import FeatureUnavailable
unavailable = FeatureUnavailable(
"tool.computer_use", ("mcp==1.26.0",), "lazy installs disabled"
"tool.computer_use", ("mcp==1.28.1",), "lazy installs disabled"
)
with patch.object(cua_backend, "_maybe_nudge_update"), \
patch("tools.lazy_deps.ensure", side_effect=unavailable), \

View file

@ -277,14 +277,14 @@ LAZY_DEPS: dict[str, tuple[str, ...]] = {
# for stripped/source-build installs that somehow dropped it. The vision
# call site uses prompt=False so it can never raise a blocking input()
# prompt mid-session (#40490).
"tool.vision": ("Pillow==12.2.0",),
"tool.vision": ("Pillow==12.3.0",),
# Computer Use (cua-driver) — the MCP client SDK used to spawn and talk
# to the cua-driver process over stdio. Matches the `mcp` / `computer-use`
# extras in pyproject.toml. The one-liner installer pulls this in via
# `[all]`; lazy-installing here covers lean / partial / broken-extra
# installs so computer_use never dead-ends on `No module named 'mcp'`.
"tool.computer_use": (
"mcp==1.26.0",
"mcp==1.28.1",
"starlette==1.3.1", # CVE-2026-48710 — keep in sync with pyproject [computer-use]
),
# HF Agent Trace Viewer upload (hermes trace upload / /upload-trace).

View file

@ -21,30 +21,21 @@
"dependencies": {
"@hermes/ink": "file:./packages/hermes-ink",
"@hermes/shared": "file:../apps/shared",
"@nanostores/react": "^1.1.0",
"ink": "^6.8.0",
"ink-text-input": "^6.0.0",
"nanostores": "^1.2.0",
"react": "^19.2.4",
"undici": "^6.25.0",
"unicode-animations": "^1.0.3"
"@nanostores/react": "1.1.0",
"ink": "6.8.0",
"ink-text-input": "6.0.0",
"nanostores": "1.4.0",
"react": "19.2.7",
"undici": "6.27.0",
"unicode-animations": "1.0.3"
},
"devDependencies": {
"@eslint/js": "^9",
"@types/node": "^22.20.0",
"@types/react": "^19.2.14",
"@typescript-eslint/eslint-plugin": "^8",
"@typescript-eslint/parser": "^8",
"esbuild": "^0.28.1",
"eslint": "^9",
"eslint-plugin-perfectionist": "^5",
"eslint-plugin-react": "^7",
"eslint-plugin-react-hooks": "^7",
"eslint-plugin-unused-imports": "^4",
"globals": "^16",
"prettier": "^3",
"tsx": "^4.22.4",
"typescript": "^6.0.3",
"vitest": "^4.1.3"
"@types/node": "22.20.1",
"@types/react": "19.2.17",
"esbuild": "0.28.1",
"prettier": "3.9.5",
"tsx": "4.23.1",
"typescript": "6.0.3",
"vitest": "4.1.10"
}
}

View file

@ -27,33 +27,33 @@
"./package.json": "./package.json"
},
"peerDependencies": {
"ink-text-input": ">=6.0.0",
"react": ">=19.0.0"
"ink-text-input": "6.0.0",
"react": "19.2.7"
},
"dependencies": {
"@alcalzone/ansi-tokenize": "^0.1.0",
"auto-bind": "^5.0.0",
"bidi-js": "^1.0.0",
"chalk": "^5.4.0",
"cli-boxes": "^3.0.0",
"code-excerpt": "^4.0.0",
"emoji-regex": "^10.4.0",
"get-east-asian-width": "^1.3.0",
"indent-string": "^5.0.0",
"lodash-es": "^4.17.0",
"react": ">=19.0.0",
"@alcalzone/ansi-tokenize": "0.1.3",
"auto-bind": "5.0.1",
"bidi-js": "1.0.3",
"chalk": "5.6.2",
"cli-boxes": "3.0.0",
"code-excerpt": "4.0.0",
"emoji-regex": "10.6.0",
"get-east-asian-width": "1.6.0",
"indent-string": "5.0.0",
"lodash-es": "4.18.1",
"react": "19.2.7",
"react-reconciler": "0.33.0",
"semver": "^7.6.0",
"signal-exit": "^4.1.0",
"stack-utils": "^2.0.0",
"strip-ansi": "^7.1.0",
"supports-hyperlinks": "^3.1.0",
"type-fest": "^4.30.0",
"usehooks-ts": "^3.1.0",
"wrap-ansi": "^9.0.0"
"semver": "7.8.5",
"signal-exit": "4.1.0",
"stack-utils": "2.0.6",
"strip-ansi": "7.2.0",
"supports-hyperlinks": "3.2.0",
"type-fest": "4.41.0",
"usehooks-ts": "3.1.1",
"wrap-ansi": "9.0.2"
},
"devDependencies": {
"esbuild": "^0.28.1",
"typescript": "^6.0.3"
"esbuild": "0.28.1",
"typescript": "6.0.3"
}
}

286
uv.lock generated
View file

@ -7,6 +7,18 @@ resolution-markers = [
"python_full_version < '3.12'",
]
[options]
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
exclude-newer-span = "P14D"
[options.exclude-newer-package]
nemo-relay = false
vercel = false
huggingface-hub = false
[manifest]
overrides = [{ name = "pynacl", specifier = ">=1.6,<1.7" }]
[[package]]
name = "agent-client-protocol"
version = "0.9.0"
@ -1845,9 +1857,9 @@ requires-dist = [
{ name = "lark-oapi", marker = "extra == 'feishu'", specifier = "==1.6.8" },
{ name = "markdown", specifier = "==3.10.2" },
{ name = "mautrix", extras = ["encryption"], marker = "extra == 'matrix'", specifier = "==0.21.0" },
{ name = "mcp", marker = "extra == 'computer-use'", specifier = "==1.26.0" },
{ name = "mcp", marker = "extra == 'dev'", specifier = "==1.26.0" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = "==1.26.0" },
{ name = "mcp", marker = "extra == 'computer-use'", specifier = "==1.28.1" },
{ name = "mcp", marker = "extra == 'dev'", specifier = "==1.28.1" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = "==1.28.1" },
{ name = "mem0ai", marker = "extra == 'mem0'", specifier = "==2.0.10" },
{ name = "microsoft-teams-apps", marker = "extra == 'teams'", specifier = "==2.0.13.4" },
{ name = "mistralai", marker = "extra == 'mistral'", specifier = "==2.4.8" },
@ -1863,14 +1875,14 @@ requires-dist = [
{ name = "packaging", specifier = "==26.0" },
{ name = "parallel-web", marker = "extra == 'parallel-web'", specifier = "==0.4.2" },
{ name = "pathspec", specifier = "==1.1.1" },
{ name = "pillow", specifier = "==12.2.0" },
{ name = "pillow", specifier = "==12.3.0" },
{ name = "prompt-toolkit", specifier = "==3.0.52" },
{ name = "psutil", specifier = "==7.2.2" },
{ name = "ptyprocess", marker = "sys_platform != 'win32'", specifier = ">=0.7.0,<1" },
{ name = "pvporcupine", marker = "extra == 'wake'", specifier = "==4.0.3" },
{ name = "pydantic", specifier = "==2.13.4" },
{ name = "pyjwt", extras = ["crypto"], specifier = "==2.13.0" },
{ name = "pytest", marker = "extra == 'dev'", specifier = "==9.0.2" },
{ name = "pytest", marker = "extra == 'dev'", specifier = "==9.1.1" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = "==1.3.0" },
{ name = "python-dotenv", specifier = "==1.2.2" },
{ name = "python-multipart", specifier = ">=0.0.9,<1" },
@ -1888,7 +1900,7 @@ requires-dist = [
{ name = "ruamel-yaml", specifier = "==0.18.17" },
{ name = "ruff", marker = "extra == 'dev'", specifier = "==0.15.10" },
{ name = "sentencepiece", marker = "extra == 'wake'", specifier = "==0.2.2" },
{ name = "setuptools", marker = "extra == 'dev'", specifier = "==81.0.0" },
{ name = "setuptools", marker = "extra == 'dev'", specifier = "==83.0.0" },
{ name = "sherpa-onnx", marker = "extra == 'wake'", specifier = "==1.13.4" },
{ name = "simple-term-menu", marker = "extra == 'cli'", specifier = "==1.6.6" },
{ name = "slack-bolt", marker = "extra == 'messaging'", specifier = "==1.29.0" },
@ -1985,14 +1997,14 @@ wheels = [
[[package]]
name = "httplib2"
version = "0.31.2"
version = "0.32.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyparsing" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz", hash = "sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", size = 250800, upload-time = "2026-01-23T11:04:44.165Z" }
sdist = { url = "https://files.pythonhosted.org/packages/84/f5/ccf58de92d61e3ad921119668f54ed36ca1d0cf5dcc5c1657dfb164fd78b/httplib2-0.32.0.tar.gz", hash = "sha256:48a0ef30a42db65d8f3399045e1d09ab0ba66e3b9efc360d07f80ea55d286025", size = 254283, upload-time = "2026-06-26T10:13:56.265Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" },
{ url = "https://files.pythonhosted.org/packages/33/a0/550eec327e5f5c7b732531c489f5307efec41f047b0d703bd4ca1e5ad2db/httplib2-0.32.0-py3-none-any.whl", hash = "sha256:dc6705cacdf3fb0a2aba7629fa33c90fd93e30035db0c157325826be177e4816", size = 93148, upload-time = "2026-06-26T10:13:54.985Z" },
]
[[package]]
@ -2362,7 +2374,7 @@ encryption = [
[[package]]
name = "mcp"
version = "1.26.0"
version = "1.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@ -2380,9 +2392,9 @@ dependencies = [
{ name = "typing-inspection" },
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" },
{ url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" },
]
[[package]]
@ -2542,37 +2554,43 @@ wheels = [
[[package]]
name = "msgpack"
version = "1.1.2"
version = "1.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" }
sdist = { url = "https://files.pythonhosted.org/packages/31/f9/c0a1c127f9049db9155afc316952ea571720dd01833ff5e4d7e8e6352dbb/msgpack-1.2.1.tar.gz", hash = "sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647", size = 183960, upload-time = "2026-06-18T16:13:52.594Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271, upload-time = "2025-10-08T09:14:49.967Z" },
{ url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914, upload-time = "2025-10-08T09:14:50.958Z" },
{ url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962, upload-time = "2025-10-08T09:14:51.997Z" },
{ url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183, upload-time = "2025-10-08T09:14:53.477Z" },
{ url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454, upload-time = "2025-10-08T09:14:54.648Z" },
{ url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341, upload-time = "2025-10-08T09:14:56.328Z" },
{ url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747, upload-time = "2025-10-08T09:14:57.882Z" },
{ url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z" },
{ url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755, upload-time = "2025-10-08T09:15:00.48Z" },
{ url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" },
{ url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" },
{ url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" },
{ url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" },
{ url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" },
{ url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" },
{ url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" },
{ url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" },
{ url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" },
{ url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" },
{ url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" },
{ url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" },
{ url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" },
{ url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" },
{ url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" },
{ url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" },
{ url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" },
{ url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" },
{ url = "https://files.pythonhosted.org/packages/f4/6b/e9b1cdc042c4458801d2545ed782a95f3d6ba8e270cce8745b8603c7f748/msgpack-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:29a3f6e9667868429d8240dfd063ea5ffdc1321c13d783aa23827a38de0dcb22", size = 82812, upload-time = "2026-06-18T16:12:45.022Z" },
{ url = "https://files.pythonhosted.org/packages/0c/3a/dd518a1bf78ed1e9ad8afe57307c079a00eafe4b3068932a27ca1ea56b4f/msgpack-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aded5bdf32609dc7987a49bbbd15a8ef096193f96dd8bbeb791de729e650acf5", size = 82739, upload-time = "2026-06-18T16:12:46.025Z" },
{ url = "https://files.pythonhosted.org/packages/70/e0/7ba9e1542bf0771a27b8b37c1316e3f95ae9d748fd765284655c476ad4ef/msgpack-1.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:146ee4e9ce80b365c6d4c47073da9da7bcec473e58194ceee5dd7620ace77e06", size = 414233, upload-time = "2026-06-18T16:12:47.029Z" },
{ url = "https://files.pythonhosted.org/packages/03/8d/671d81534ea0e2b0e8a121be100020da09eb78861fe3aa8f3ef7dcd3bed1/msgpack-1.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a28d076ca7c82b9c8728ad90b7147489449557038bed50e4241eb832395169b4", size = 423843, upload-time = "2026-06-18T16:12:48.19Z" },
{ url = "https://files.pythonhosted.org/packages/d2/b6/e5c737515ed1f166664b87601b532f58cbb73d8aa6a90b99f7c2c5037e8e/msgpack-1.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d31c0ac0c640f877804c67cb2bc9f4e23dc2db97e96c2e67fa27d38283b41f8", size = 390772, upload-time = "2026-06-18T16:12:49.624Z" },
{ url = "https://files.pythonhosted.org/packages/a8/46/62ed8c2e87d7021eab19921594d961ef3aa3794eec76c716dc30f3bfd433/msgpack-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ff92d7feeaf5bc26c51495b69e2f99ed97ab79346fb6555f44be7dd2ac6503b", size = 409559, upload-time = "2026-06-18T16:12:50.936Z" },
{ url = "https://files.pythonhosted.org/packages/70/ff/59aa3887b860bbf43532835e192b1c388a17590d6068ae4f8b2bc74c906e/msgpack-1.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:779197a6513bab3c3632265e3d0f7cb3227e62510841a6f34f1eaa37efbb345e", size = 387838, upload-time = "2026-06-18T16:12:52.161Z" },
{ url = "https://files.pythonhosted.org/packages/09/11/f8563e471093420cf6478cb3271a0175d8402b82d879783d4035d2d03360/msgpack-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67f6dd22fa72a93752643f07889796d62739a13415ee630169a8ce764f86cf9f", size = 421732, upload-time = "2026-06-18T16:12:53.556Z" },
{ url = "https://files.pythonhosted.org/packages/57/cf/e673683c4c6c90c1022b24c65af4b03eda72b182a1176ef6449069d66acc/msgpack-1.2.1-cp311-cp311-win32.whl", hash = "sha256:91054a783328e0ea7954b8771095705c8d2243b814743fbaadf14552c9c52c5d", size = 64091, upload-time = "2026-06-18T16:12:54.821Z" },
{ url = "https://files.pythonhosted.org/packages/3f/07/ca212739d179f9083bff2c7c08c24101c3555a334fadc2b876b18768a3ae/msgpack-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2eda0b7ebb1283a98d3e4492ac933c8af6aff59fd3df1c3ed024f536af4b1dc8", size = 70462, upload-time = "2026-06-18T16:12:55.898Z" },
{ url = "https://files.pythonhosted.org/packages/6d/be/6798347b425e26f35db82e69dd83c09716c856a3714e7bffc4c0860fd830/msgpack-1.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ee967f7c7e1df2890c671ff2ee51a28ded0efc95da3e507176dee881ce36c66", size = 65059, upload-time = "2026-06-18T16:12:57.053Z" },
{ url = "https://files.pythonhosted.org/packages/bc/dd/9e8cbd8f5582ca4b590336f2b91ee5662f6a6ca562b565abaf696a0f81ff/msgpack-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ef59c659f289eddf8aa6623823f19fa2f40a4029266889eac7a2505dd210c35", size = 83531, upload-time = "2026-06-18T16:12:58.249Z" },
{ url = "https://files.pythonhosted.org/packages/50/2e/ebdb85a8da151397a2790363676b7ed7c125924fe618e4c6d8befb0cc62c/msgpack-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3567748a5107cb40cdf66a275430c2f87c07777698f4bfd25c35f44d533258c", size = 82657, upload-time = "2026-06-18T16:12:59.396Z" },
{ url = "https://files.pythonhosted.org/packages/26/aa/753ad8b007b464e1d8aa0c8e650b9c5f4f725e658fc5ac8a7635c55b7f6e/msgpack-1.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60926b75d00c8e816ef98f3034f484a8bc64242d66839cef4cf7e503142316a0", size = 410634, upload-time = "2026-06-18T16:13:00.383Z" },
{ url = "https://files.pythonhosted.org/packages/6a/fd/6adabd4f6d5e686f97dd02ce7fce3fe4cf672cbac36b8f67ff4040e8ad8b/msgpack-1.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:020e881a764b20d8d7ca1a54fc01b8175519d108e3c3f194fddc200bda95951a", size = 419989, upload-time = "2026-06-18T16:13:01.776Z" },
{ url = "https://files.pythonhosted.org/packages/5a/cc/85039b7b0eb168aaad7383a23c97e291a11f08351cb45a606ce865e4e3f1/msgpack-1.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4202c74688ca06591f78cb18988228bd4cca2cc75d57b60008372892d2f1e6e6", size = 377544, upload-time = "2026-06-18T16:13:03.637Z" },
{ url = "https://files.pythonhosted.org/packages/ed/bf/35963899493b32030c85fc513b723ae66144ac70c11ebc52e889e16e3d99/msgpack-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8b267ce94efb76fbd1b3373511420074ee3187f0f7811bf394531de13294735a", size = 400842, upload-time = "2026-06-18T16:13:05.012Z" },
{ url = "https://files.pythonhosted.org/packages/a6/df/8e2ac970c8f99264cd9997d1c73df5466bc19da3301d7dc5500862a9b089/msgpack-1.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f1d0f8f98ade9634e01fb704a408f9336c0a8f1117b369f5db83dc7551d8b1", size = 374108, upload-time = "2026-06-18T16:13:06.232Z" },
{ url = "https://files.pythonhosted.org/packages/17/dd/fa8bd265110dfa51c20cb529f9e6d240a16fafe7e645004c6af2d01353ba/msgpack-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f02cf17a6ca1abe29b5f980644f7551f94d71f2011509b26d8625ce038f0df64", size = 414939, upload-time = "2026-06-18T16:13:07.478Z" },
{ url = "https://files.pythonhosted.org/packages/2e/b9/8377a5ad8953fc0437c70cc98d9ae29f27fe5ac5109fbec0812085865735/msgpack-1.2.1-cp312-cp312-win32.whl", hash = "sha256:0c0d9802354507bcba62af19c17918e3eb437cc25e6f50657d511b5856a77aac", size = 64504, upload-time = "2026-06-18T16:13:08.822Z" },
{ url = "https://files.pythonhosted.org/packages/57/7f/ce1e377df7e62461fefd9eb23bfb93a4a523f40a517b377b8f844d836828/msgpack-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c24aa15d5963051e1a5c62b12c50cd705992502b5ec1f3bece6046f33c9fc24", size = 71421, upload-time = "2026-06-18T16:13:09.828Z" },
{ url = "https://files.pythonhosted.org/packages/8f/32/ebfe84c9929f08f188d56c7a2fd913406a9ddad76a634697c1c43b8112e6/msgpack-1.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4227224aaec8f7fbcbfbd4272319347b2bb4030366502600f8c45588c5187b07", size = 64775, upload-time = "2026-06-18T16:13:11.056Z" },
{ url = "https://files.pythonhosted.org/packages/b0/ac/dcddcab6f6c20ecb387ca5e980371cdb3f87ff69aeca388be97eebc4c074/msgpack-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064", size = 83151, upload-time = "2026-06-18T16:13:12.173Z" },
{ url = "https://files.pythonhosted.org/packages/64/71/fbcfa83a1d6a9c6091942d1cfd070962244664b87427a9a49a6897b1b219/msgpack-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056", size = 82351, upload-time = "2026-06-18T16:13:13.194Z" },
{ url = "https://files.pythonhosted.org/packages/e3/10/ddf7b06db879e8792d13934ddda09ff20bd2a583fd84c9b59aae9b0e650b/msgpack-1.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc", size = 407518, upload-time = "2026-06-18T16:13:14.233Z" },
{ url = "https://files.pythonhosted.org/packages/79/d3/36a46a8ed992b781acbc05928bd5bee3c810cb0c3563bf81a7b0c04a1a76/msgpack-1.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d", size = 416405, upload-time = "2026-06-18T16:13:15.435Z" },
{ url = "https://files.pythonhosted.org/packages/f9/84/e8e9598b557c0ba6ddae901a73780a4c75ac667dddf59414b1e56a42fb34/msgpack-1.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155", size = 376257, upload-time = "2026-06-18T16:13:17.022Z" },
{ url = "https://files.pythonhosted.org/packages/40/16/738fe6d875ad7e2a9429c165322a4ec088f4f273cdfae63d96a89c467961/msgpack-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402", size = 397469, upload-time = "2026-06-18T16:13:18.287Z" },
{ url = "https://files.pythonhosted.org/packages/ca/be/6d5952df75a7f24f35833af764c3a6860780364cb3a0030beb8099e1b2b4/msgpack-1.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c", size = 372802, upload-time = "2026-06-18T16:13:19.685Z" },
{ url = "https://files.pythonhosted.org/packages/e1/39/e2ef7dbf0473bcb8dc7c50bf782a892d67414877b63e47fc88eb189ef5e6/msgpack-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6", size = 411273, upload-time = "2026-06-18T16:13:21.028Z" },
{ url = "https://files.pythonhosted.org/packages/ef/c5/133f4512a56e983a93445c836c9d94d88f3bc2e0980ff4b9e577bd8416ce/msgpack-1.2.1-cp313-cp313-win32.whl", hash = "sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707", size = 64471, upload-time = "2026-06-18T16:13:22.293Z" },
{ url = "https://files.pythonhosted.org/packages/e2/98/577e10b055096a7dd40732358cabaf7180a20c79ed1dcdbb618e4b9deac7/msgpack-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9", size = 71274, upload-time = "2026-06-18T16:13:23.455Z" },
{ url = "https://files.pythonhosted.org/packages/ba/ee/0c0048e7cfbef23c6a94791b8959ab28155232e7956de8a305b5ff588f05/msgpack-1.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a", size = 64795, upload-time = "2026-06-18T16:13:24.687Z" },
]
[[package]]
@ -3031,64 +3049,45 @@ wheels = [
[[package]]
name = "pillow"
version = "12.2.0"
version = "12.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" },
{ url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" },
{ url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" },
{ url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" },
{ url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" },
{ url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" },
{ url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" },
{ url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" },
{ url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" },
{ url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" },
{ url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" },
{ url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" },
{ url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" },
{ url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" },
{ url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" },
{ url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" },
{ url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" },
{ url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" },
{ url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" },
{ url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" },
{ url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" },
{ url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" },
{ url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" },
{ url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" },
{ url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" },
{ url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" },
{ url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" },
{ url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" },
{ url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" },
{ url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" },
{ url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" },
{ url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" },
{ url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" },
{ url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" },
{ url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" },
{ url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" },
{ url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" },
{ url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" },
{ url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" },
{ url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" },
{ url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" },
{ url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" },
{ url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" },
{ url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" },
{ url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" },
{ url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" },
{ url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" },
{ url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" },
{ url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" },
{ url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" },
{ url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" },
{ url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" },
{ url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" },
{ url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" },
{ url = "https://files.pythonhosted.org/packages/fb/c8/0a78b0e02d7ac54bc03e5321c9220da52f0c2ea83b21f7c40e7f3169c502/pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756", size = 5392415, upload-time = "2026-07-01T11:53:47.162Z" },
{ url = "https://files.pythonhosted.org/packages/b2/5b/a02d30018abd97ced9f5a6c63d28597694a00d066516b9c1c6de45859fc9/pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6", size = 4785266, upload-time = "2026-07-01T11:53:49.079Z" },
{ url = "https://files.pythonhosted.org/packages/c8/98/766667a4be768150a202836acd9fad19c06824ca86c4286d3cf6b274964e/pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd", size = 6263814, upload-time = "2026-07-01T11:53:51.32Z" },
{ url = "https://files.pythonhosted.org/packages/3b/2d/ede717bc1144f63886c21fd349bb95860b0d1a21149ff16f2bb362b612b6/pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd", size = 6934408, upload-time = "2026-07-01T11:53:53.487Z" },
{ url = "https://files.pythonhosted.org/packages/a3/48/9c58b685e69d49c31af6c8eb9012055fab7e665785165c84796e2c73ce72/pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c", size = 6337160, upload-time = "2026-07-01T11:53:55.457Z" },
{ url = "https://files.pythonhosted.org/packages/ff/fa/dc2a5c0ba6df93f67c31d34b808b7ce440b40cdbf96f0b81cde1d1e6fa93/pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5", size = 7045172, upload-time = "2026-07-01T11:53:57.736Z" },
{ url = "https://files.pythonhosted.org/packages/86/a5/444817a4d4c4c2417df00513086ca196f388d8f9ef40c2e4ccd1ad1af54b/pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b", size = 6472232, upload-time = "2026-07-01T11:53:59.767Z" },
{ url = "https://files.pythonhosted.org/packages/63/c6/4bad1b18d132a50b27e1365e1ab163616f7a5bb56d330f66f9d1d9d4f9d4/pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a", size = 7233653, upload-time = "2026-07-01T11:54:02.066Z" },
{ url = "https://files.pythonhosted.org/packages/fd/16/00f91ab7760dc842f5aad55217e80fc4a7067a0604535249bc8a2d6d9870/pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26", size = 2568195, upload-time = "2026-07-01T11:54:04.622Z" },
{ url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" },
{ url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" },
{ url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" },
{ url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" },
{ url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" },
{ url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" },
{ url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" },
{ url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" },
{ url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" },
{ url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" },
{ url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" },
{ url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" },
{ url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" },
{ url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" },
{ url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" },
{ url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" },
{ url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" },
{ url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" },
{ url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" },
{ url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" },
{ url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" },
{ url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" },
{ url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" },
{ url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" },
{ url = "https://files.pythonhosted.org/packages/08/14/f6ba68107680ffa74b39985f3f30884e41318fbc4250caa423c79b4788bb/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3", size = 5860358, upload-time = "2026-07-01T11:56:32.68Z" },
{ url = "https://files.pythonhosted.org/packages/36/54/0169bc772ec491108b62f644f8ecf1fe5d8ae5ebafde2ee2142210166903/pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a", size = 7231786, upload-time = "2026-07-01T11:56:35.046Z" },
]
[[package]]
@ -3280,11 +3279,11 @@ wheels = [
[[package]]
name = "pyasn1"
version = "0.6.3"
version = "0.6.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" }
sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" },
{ url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" },
]
[[package]]
@ -3427,25 +3426,25 @@ wheels = [
[[package]]
name = "pydantic-settings"
version = "2.13.1"
version = "2.14.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "python-dotenv" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" },
{ url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" },
]
[[package]]
name = "pygments"
version = "2.19.2"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
@ -3464,22 +3463,25 @@ crypto = [
[[package]]
name = "pynacl"
version = "1.5.0"
version = "1.6.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi" },
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854, upload-time = "2022-01-07T22:05:41.134Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920, upload-time = "2022-01-07T22:05:49.156Z" },
{ url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722, upload-time = "2022-01-07T22:05:50.989Z" },
{ url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087, upload-time = "2022-01-07T22:05:52.539Z" },
{ url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678, upload-time = "2022-01-07T22:05:54.251Z" },
{ url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660, upload-time = "2022-01-07T22:05:56.056Z" },
{ url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824, upload-time = "2022-01-07T22:05:57.434Z" },
{ url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912, upload-time = "2022-01-07T22:05:58.665Z" },
{ url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624, upload-time = "2022-01-07T22:06:00.085Z" },
{ url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141, upload-time = "2022-01-07T22:06:01.861Z" },
{ url = "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", size = 388458, upload-time = "2026-01-01T17:32:16.829Z" },
{ url = "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", size = 800020, upload-time = "2026-01-01T17:32:18.34Z" },
{ url = "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", size = 1399174, upload-time = "2026-01-01T17:32:20.239Z" },
{ url = "https://files.pythonhosted.org/packages/68/f7/322f2f9915c4ef27d140101dd0ed26b479f7e6f5f183590fd32dfc48c4d3/pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87", size = 835085, upload-time = "2026-01-01T17:32:22.24Z" },
{ url = "https://files.pythonhosted.org/packages/3e/d0/f301f83ac8dbe53442c5a43f6a39016f94f754d7a9815a875b65e218a307/pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c", size = 1437614, upload-time = "2026-01-01T17:32:23.766Z" },
{ url = "https://files.pythonhosted.org/packages/c4/58/fc6e649762b029315325ace1a8c6be66125e42f67416d3dbd47b69563d61/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130", size = 818251, upload-time = "2026-01-01T17:32:25.69Z" },
{ url = "https://files.pythonhosted.org/packages/c9/a8/b917096b1accc9acd878819a49d3d84875731a41eb665f6ebc826b1af99e/pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6", size = 1402859, upload-time = "2026-01-01T17:32:27.215Z" },
{ url = "https://files.pythonhosted.org/packages/85/42/fe60b5f4473e12c72f977548e4028156f4d340b884c635ec6b063fe7e9a5/pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e", size = 791926, upload-time = "2026-01-01T17:32:29.314Z" },
{ url = "https://files.pythonhosted.org/packages/fa/f9/e40e318c604259301cc091a2a63f237d9e7b424c4851cafaea4ea7c4834e/pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577", size = 1363101, upload-time = "2026-01-01T17:32:31.263Z" },
{ url = "https://files.pythonhosted.org/packages/48/47/e761c254f410c023a469284a9bc210933e18588ca87706ae93002c05114c/pynacl-1.6.2-cp38-abi3-win32.whl", hash = "sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa", size = 227421, upload-time = "2026-01-01T17:32:33.076Z" },
{ url = "https://files.pythonhosted.org/packages/41/ad/334600e8cacc7d86587fe5f565480fde569dfb487389c8e1be56ac21d8ac/pynacl-1.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0", size = 239754, upload-time = "2026-01-01T17:32:34.557Z" },
{ url = "https://files.pythonhosted.org/packages/29/7d/5945b5af29534641820d3bd7b00962abbbdfee84ec7e19f0d5b3175f9a31/pynacl-1.6.2-cp38-abi3-win_arm64.whl", hash = "sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c", size = 184801, upload-time = "2026-01-01T17:32:36.309Z" },
]
[[package]]
@ -3502,7 +3504,7 @@ wheels = [
[[package]]
name = "pytest"
version = "9.0.2"
version = "9.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@ -3511,9 +3513,9 @@ dependencies = [
{ name = "pluggy" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
]
[[package]]
@ -3983,7 +3985,7 @@ resolution-markers = [
"python_full_version < '3.12'",
]
dependencies = [
{ name = "numpy", marker = "python_full_version < '3.12'" },
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
wheels = [
@ -4038,7 +4040,7 @@ resolution-markers = [
"python_full_version == '3.12.*'",
]
dependencies = [
{ name = "numpy", marker = "python_full_version >= '3.12'" },
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" }
wheels = [
@ -4102,11 +4104,11 @@ wheels = [
[[package]]
name = "setuptools"
version = "81.0.0"
version = "83.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" }
sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" },
{ url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" },
]
[[package]]
@ -4398,19 +4400,19 @@ wheels = [
[[package]]
name = "tornado"
version = "6.5.5"
version = "6.5.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" }
sdist = { url = "https://files.pythonhosted.org/packages/64/24/95ec527ad67b76d59299e5465b3935d05e4294b7e0290a3924b7487df30b/tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2", size = 519252, upload-time = "2026-06-08T17:34:51.232Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" },
{ url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" },
{ url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" },
{ url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" },
{ url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" },
{ url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" },
{ url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" },
{ url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" },
{ url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" },
{ url = "https://files.pythonhosted.org/packages/02/dc/c7043cab6fed8ae159fc1923ce829ada35c4dbd797d408a43858ffaf9639/tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163", size = 448543, upload-time = "2026-06-08T17:34:38.052Z" },
{ url = "https://files.pythonhosted.org/packages/92/4f/090b1431e5a43df696feceffc268c5383cc079ecb5f08ce58f917109aafe/tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100", size = 446707, upload-time = "2026-06-08T17:34:39.594Z" },
{ url = "https://files.pythonhosted.org/packages/37/d8/ef374952fd5da67d4463122c2b8e5a96536ec10b4b339254c6dcde81d01c/tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972", size = 449774, upload-time = "2026-06-08T17:34:41.204Z" },
{ url = "https://files.pythonhosted.org/packages/35/37/d434c73f4c6e014b745b9b37085f34f40c022f007efff3d7fe65991899f3/tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b", size = 450745, upload-time = "2026-06-08T17:34:42.531Z" },
{ url = "https://files.pythonhosted.org/packages/b6/2b/56b9aff361d7f1ab728a805ec7d7ea835f8807afa9f5cc690ea0e630efb9/tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92", size = 450578, upload-time = "2026-06-08T17:34:43.787Z" },
{ url = "https://files.pythonhosted.org/packages/02/30/a7444fb23aa76860a14198fab96ac79f1866b0a6e19e26c4381b0938e50f/tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5", size = 449985, upload-time = "2026-06-08T17:34:45.326Z" },
{ url = "https://files.pythonhosted.org/packages/5c/42/5f0e56c01e8d9d36f4e23f367b85ae6cae0c1ecddd5e6977d8388ad27488/tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4", size = 451047, upload-time = "2026-06-08T17:34:46.784Z" },
{ url = "https://files.pythonhosted.org/packages/c9/a4/b393076ffb21b469eec5b328a0534cf03a3b90bfc6b1f09507cdd075d938/tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4", size = 451485, upload-time = "2026-06-08T17:34:48.248Z" },
{ url = "https://files.pythonhosted.org/packages/71/2e/7b1c769803121b809112cf9a00681c472eae1d80e32d7ec0e0bd61d0d0e1/tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796", size = 450506, upload-time = "2026-06-08T17:34:49.702Z" },
]
[[package]]
@ -4677,11 +4679,11 @@ name = "vercel-workers"
version = "0.0.25"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio", marker = "python_full_version >= '3.12'" },
{ name = "httpx", marker = "python_full_version >= '3.12'" },
{ name = "pydantic", marker = "python_full_version >= '3.12'" },
{ name = "python-dotenv", marker = "python_full_version >= '3.12'" },
{ name = "vercel", marker = "python_full_version >= '3.12'" },
{ name = "anyio" },
{ name = "httpx" },
{ name = "pydantic" },
{ name = "python-dotenv" },
{ name = "vercel" },
]
sdist = { url = "https://files.pythonhosted.org/packages/30/df/04d37021ad7ca53b7599c313e411d91623c7a005c741f491d1eefb7a9f0c/vercel_workers-0.0.25.tar.gz", hash = "sha256:212ded01400b524be51d251df49f801caf115ad7d48cca7eb168cbeceda3def3", size = 64149, upload-time = "2026-06-20T19:26:27.177Z" }
wheels = [

View file

@ -16,44 +16,39 @@
},
"dependencies": {
"@hermes/shared": "file:../apps/shared",
"@nous-research/ui": "0.18.2",
"@observablehq/plot": "^0.6.17",
"@react-three/fiber": "^9.6.0",
"@tailwindcss/vite": "^4.2.1",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-unicode11": "^0.9.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"gsap": "^3.15.0",
"leva": "^0.10.1",
"lucide-react": "^0.577.0",
"motion": "^12.38.0",
"qrcode": "^1.5.4",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.17.0",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.1",
"unicode-animations": "^1.0.3"
"@nous-research/ui": "0.16.0",
"@observablehq/plot": "0.6.17",
"@react-three/fiber": "9.6.1",
"@tailwindcss/vite": "4.3.3",
"@xterm/addon-fit": "0.11.0",
"@xterm/addon-unicode11": "0.9.0",
"@xterm/addon-web-links": "0.12.0",
"@xterm/addon-webgl": "0.19.0",
"@xterm/xterm": "6.0.0",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"gsap": "3.15.0",
"leva": "0.10.1",
"lucide-react": "0.577.0",
"motion": "12.42.2",
"qrcode": "1.5.4",
"react": "19.2.7",
"react-dom": "19.2.7",
"react-router": "8.3.0",
"tailwind-merge": "3.6.0",
"tailwindcss": "4.3.3",
"unicode-animations": "1.0.3"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/node": "^22.20.0",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"three": "^0.180.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.56.1",
"vite": "^8.0.16",
"vitest": "^4.1.5"
"@types/node": "22.20.1",
"@types/qrcode": "1.5.6",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "6.0.3",
"eslint-plugin-react-refresh": "0.5.3",
"three": "0.180.0",
"typescript": "6.0.3",
"vite": "8.2.0",
"vitest": "4.1.10"
}
}

View file

@ -19,7 +19,7 @@ import {
Navigate,
useLocation,
useNavigate,
} from "react-router-dom";
} from "react-router";
import {
Activity,
BarChart3,

View file

@ -23,7 +23,7 @@ import { ListItem } from "@nous-research/ui/ui/components/list-item";
import { Spinner } from "@nous-research/ui/ui/components/spinner";
import { AlertCircle, MessageSquarePlus, RefreshCw } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { useSearchParams } from "react-router";
import { useI18n } from "@/i18n";
import { api, type SessionInfo } from "@/lib/api";

View file

@ -1,4 +1,4 @@
import { Link } from "react-router-dom";
import { Link } from "react-router";
import type { StatusResponse } from "@/lib/api";
import { cn } from "@/lib/utils";
import { useI18n } from "@/i18n";

View file

@ -1,5 +1,5 @@
import { useLayoutEffect, useMemo, useState, type ReactNode } from "react";
import { useLocation } from "react-router-dom";
import { useLocation } from "react-router";
import { PageHeaderContext } from "./page-header-context";
import { resolvePageTitle } from "@/lib/resolve-page-title";
import { cn } from "@/lib/utils";

View file

@ -5,7 +5,7 @@ import {
useState,
type ReactNode,
} from "react";
import { useLocation, useSearchParams } from "react-router-dom";
import { useLocation, useSearchParams } from "react-router";
import { api, setManagementProfile } from "@/lib/api";
import { ProfileContext } from "@/contexts/profile-context";

View file

@ -1,5 +1,5 @@
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { BrowserRouter } from "react-router";
import "./index.css";
import App from "./App";
import { SystemActionsProvider } from "./contexts/SystemActions";

View file

@ -28,7 +28,7 @@ import { cn } from "@/lib/utils";
import { Copy, PanelRight, RotateCcw, X } from "lucide-react";
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { useSearchParams } from "react-router-dom";
import { useSearchParams } from "react-router";
import { ChatSidebar } from "@/components/ChatSidebar";
import { ChatSessionList } from "@/components/ChatSessionList";

View file

@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from "react";
import { ExternalLink, RefreshCw, Trash2, Eye, EyeOff } from "lucide-react";
import type { Translations } from "@/i18n/types";
import { Link } from "react-router-dom";
import { Link } from "react-router";
import { api } from "@/lib/api";
import type {
HubAgentPluginRow,

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import { H2 } from "@nous-research/ui/ui/components/typography/h2";
import { Card, CardContent } from "@nous-research/ui/ui/components/card";
import { Badge } from "@nous-research/ui/ui/components/badge";

View file

@ -6,7 +6,7 @@ import {
useRef,
useState,
} from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import { useProfileScope } from "@/contexts/useProfileScope";
import {
AlignLeft,

View file

@ -6,7 +6,7 @@ import {
useCallback,
useRef,
} from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import {
AlertTriangle,
CheckCircle2,

View file

@ -1,5 +1,5 @@
import { useEffect, useLayoutEffect, useState, useMemo, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate } from "react-router";
import {
Package,
Search,

View file

@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { Link } from "react-router";
import {
Activity,
Brain,

View file

@ -101,7 +101,7 @@ export default defineConfig({
groups: [
{
name: "react-vendor",
test: /node_modules[\\/](react|react-dom|scheduler|react-router|react-router-dom)([\\/]|$)/,
test: /node_modules[\\/](react|react-dom|scheduler|react-router|react-router)([\\/]|$)/,
},
{
name: "xterm",

13
website/.npmrc Normal file
View file

@ -0,0 +1,13 @@
# needed to prevent bad npm that has min-release-age but not exclude
engine-strict=true
min-release-age=14
# fast-uri 3.1.4 includes fixes for vulns. remove this when 3.1.4 is > 2wks old
min-release-age-exclude[]=fast-uri
# minimatch 10.2.6 includes fixes for vulns. remove this when 10.2.6 is > 2wks old
min-release-age-exclude[]=minimatch
# brace-expansion 5.0.8 includes fixes for vulns. remove this when 5.0.8 is > 2wks old
min-release-age-exclude[]=brace-expansion

3537
website/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,26 +18,27 @@
"lint:diagrams": "ascii-guard lint --exclude-code-blocks docs"
},
"dependencies": {
"@docusaurus/core": "3.9.2",
"@docusaurus/plugin-client-redirects": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@docusaurus/theme-mermaid": "^3.9.2",
"@easyops-cn/docusaurus-search-local": "^0.55.1",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"@docusaurus/core": "3.10.2",
"@docusaurus/plugin-client-redirects": "3.10.2",
"@docusaurus/preset-classic": "3.10.2",
"@docusaurus/theme-mermaid": "3.10.2",
"@easyops-cn/docusaurus-search-local": "0.55.2",
"@mdx-js/react": "3.1.1",
"clsx": "2.1.1",
"prism-react-renderer": "2.3.0",
"react": "19.2.7",
"react-dom": "19.2.7"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/tsconfig": "3.9.2",
"@docusaurus/types": "3.9.2",
"typescript": "^6.0.3"
"@docusaurus/module-type-aliases": "3.10.2",
"@docusaurus/tsconfig": "3.10.2",
"@docusaurus/types": "3.10.2",
"typescript": "6.0.3"
},
"overrides": {
"serialize-javascript": "^7.0.5",
"uuid": "^14.0.0"
"serialize-javascript": "7.0.7",
"uuid": "14.0.1",
"minimatch": "10.2.6"
},
"browserslist": {
"production": [
@ -52,6 +53,11 @@
]
},
"engines": {
"node": ">=20.0"
"node": ">=20.0",
"npm": "<11.10.0 || >=12.0.0"
},
"allowScripts": {
"core-js@3.49.0": true,
"core-js-pure@3.49.0": true
}
}