icon creation and freshenss check
This commit is contained in:
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@@ -175,6 +175,11 @@ jobs:
|
||||
needs: detect
|
||||
uses: ./.github/workflows/profile-artifact-check.yml
|
||||
|
||||
icons-freshness-check:
|
||||
name: Icon assets freshness
|
||||
needs: detect
|
||||
uses: ./.github/workflows/icons-freshness-check.yml
|
||||
|
||||
case-collision-check:
|
||||
name: Check no case-colliding filenames
|
||||
needs: detect
|
||||
@@ -247,6 +252,7 @@ jobs:
|
||||
- lockfile-diff
|
||||
- docker-lint
|
||||
- profile-artifact-check
|
||||
- icons-freshness-check
|
||||
- supply-chain
|
||||
- review-labels
|
||||
- osv-scanner
|
||||
|
||||
45
.github/workflows/icons-freshness-check.yml
vendored
Normal file
45
.github/workflows/icons-freshness-check.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Icon Assets Freshness
|
||||
|
||||
# Guard for the single-master icon pipeline. assets/icon-master.svg is the
|
||||
# source of truth for all 23 generated icons (see scripts/generate_icons.py);
|
||||
# this check regenerates every target and byte-compares against the committed
|
||||
# files. The repo can never hold a hand-edited or stale generated icon, and a
|
||||
# master change can never land without its regenerated outputs.
|
||||
#
|
||||
# Byte-determinism note: resvg renders the same SVG to identical bytes on any
|
||||
# host for pure-shape artwork. If the master ever contains <text>, font
|
||||
# rendering can differ across OSes — pin fonts via resvg's font_files/font_dirs
|
||||
# and commit a known font before relying on this check for that master.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Generated icons match master
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # 8.2.0
|
||||
with:
|
||||
# Pinned: unpinned setup-uv fetches a 'latest' manifest from
|
||||
# raw.githubusercontent.com every job; transient fetch failures
|
||||
# fail the job (2026-07-28 incident). Keep in sync with tests.yml.
|
||||
version: "0.9.28"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: |
|
||||
pyproject.toml
|
||||
uv.lock
|
||||
|
||||
# Pillow is a core dependency; resvg-py rides the dev extra.
|
||||
- name: Install locked deps
|
||||
run: uv sync --locked --python 3.11 --extra dev
|
||||
|
||||
- name: Verify generated icons are in sync with the master
|
||||
run: uv run python scripts/generate_icons.py --check
|
||||
4
assets/icon-master.svg
Normal file
4
assets/icon-master.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="245" fill="#ffffff"/>
|
||||
<circle cx="512" cy="512" r="420" fill="#e34c4c"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 198 B |
@@ -199,7 +199,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.1.1", "pytest-asyncio==1.3.0", "mcp==2.0.0", "httpx2==2.7.0", "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)
|
||||
dev = ["debugpy==1.8.20", "pytest==9.1.1", "pytest-asyncio==1.3.0", "mcp==2.0.0", "httpx2==2.7.0", "starlette==1.3.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==83.0.0", "resvg-py==0.4.0"] # starlette: CVE-2026-48710; setuptools: 83 (torch >=2.13 requires setuptools 83)
|
||||
messaging = ["python-telegram-bot[webhooks]==22.8", "discord.py[voice]==2.7.1", "aiohttp==3.14.3", "brotlicffi==1.2.0.1", "slack-bolt==1.30.0", "slack-sdk==3.43.0", "qrcode==7.4.2"] # aiohttp 3.14.3: prior CVEs + GHSA-cq5v-8q36-5273/GHSA-mfx4-hv73-q22v/GHSA-mq44-7p77-q5h7
|
||||
cron = [] # croniter is now a core dependency; this extra kept for back-compat
|
||||
slack = ["slack-bolt==1.30.0", "slack-sdk==3.43.0", "aiohttp==3.14.3"]
|
||||
|
||||
272
scripts/generate_icons.py
Normal file
272
scripts/generate_icons.py
Normal file
@@ -0,0 +1,272 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate every app icon in the repo from a single master SVG.
|
||||
|
||||
Usage (from repo root):
|
||||
.venv/Scripts/python.exe scripts/generate_icons.py # write
|
||||
.venv/Scripts/python.exe scripts/generate_icons.py --check # verify only
|
||||
|
||||
Master: assets/icon-master.svg
|
||||
A 1024x1024 SVG. The background must be a WHITE ROUNDED SQUARE
|
||||
(rx ~ 245, spanning the full canvas) with the artwork on top —
|
||||
the transparent corners outside the squircle are what give the
|
||||
icons their app-icon shape everywhere downstream. If the master
|
||||
is missing, a red-circle placeholder is written automatically;
|
||||
drop in the real artwork and re-run.
|
||||
|
||||
--check mode regenerates every target in memory and byte-compares it
|
||||
against the committed file, exiting nonzero on any drift. This is what
|
||||
CI runs (see .github/workflows/icons-freshness-check.yml) so the repo
|
||||
can never hold a hand-edited or stale generated icon.
|
||||
|
||||
Rendering: resvg (resvg-py) for SVG -> PNG fidelity at every size.
|
||||
Containers: Pillow for multi-size .ico and .icns.
|
||||
|
||||
Deps:
|
||||
Pillow (core dependency), resvg-py (dev extra):
|
||||
uv sync --extra dev
|
||||
|
||||
Outputs (23 files, all measured against the originals):
|
||||
apps/desktop/assets/icon.png 1024x1024 squircle
|
||||
apps/desktop/assets/icon.ico 16,24,32,48,64,128,256
|
||||
apps/desktop/assets/icon.icns 16..1024 (real ICNS)
|
||||
apps/desktop/assets/appx/Wide310x150Logo.png 310x150, squircle 100 centered
|
||||
apps/desktop/assets/appx/StoreLogo.png 50x50 squircle
|
||||
apps/desktop/assets/appx/Square44x44Logo.png 44x44 squircle
|
||||
apps/desktop/assets/appx/Square150x150Logo.png 150x150 squircle
|
||||
apps/desktop/public/apple-touch-icon.png 1024x1024 squircle
|
||||
apps/desktop/public/nous-girl.jpg 256x256 JPEG on white
|
||||
apps/bootstrap-installer/src-tauri/icons/32x32.png 32x32
|
||||
apps/bootstrap-installer/src-tauri/icons/128x128.png 128x128
|
||||
apps/bootstrap-installer/src-tauri/icons/128x128@2x.png 256x256
|
||||
apps/bootstrap-installer/src-tauri/icons/icon.ico 16,32,64,128,256
|
||||
apps/bootstrap-installer/src-tauri/icons/icon.icns 16..1024
|
||||
apps/bootstrap-installer/public/nous-girl.jpg 256x256 JPEG on white
|
||||
website/static/img/logo.png 1772x1799 black-frame wordmark
|
||||
website/static/img/nous-logo.png 150x150 on white (opaque)
|
||||
website/static/img/favicon-16x16.png 16x16
|
||||
website/static/img/favicon-32x32.png 32x32
|
||||
website/static/img/apple-touch-icon.png 180x180
|
||||
website/static/img/favicon.ico 16,32,48
|
||||
website/static/img/favicon.svg copy of the master
|
||||
web/public/favicon.ico 16,32,48
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image
|
||||
|
||||
try:
|
||||
import resvg_py
|
||||
except ImportError:
|
||||
sys.exit(
|
||||
"resvg-py is missing. Install it with:\n"
|
||||
" uv sync --extra dev"
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
MASTER = ROOT / "assets" / "icon-master.svg"
|
||||
|
||||
# The squircle corner radius measured on the real 1024 icon (~245px).
|
||||
SQUIRCLE_RADIUS = 245
|
||||
|
||||
PLACEHOLDER = f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="{SQUIRCLE_RADIUS}" fill="#ffffff"/>
|
||||
<circle cx="512" cy="512" r="420" fill="#e34c4c"/>
|
||||
</svg>
|
||||
'''
|
||||
|
||||
# (relpath, kind, arg)
|
||||
TARGETS: list[tuple[str, str, object]] = [
|
||||
("apps/desktop/assets/icon.png", "png", 1024),
|
||||
("apps/desktop/assets/icon.ico", "ico", [16, 24, 32, 48, 64, 128, 256]),
|
||||
("apps/desktop/assets/icon.icns", "icns", None),
|
||||
("apps/desktop/assets/appx/Wide310x150Logo.png", "wide", (310, 150)),
|
||||
("apps/desktop/assets/appx/StoreLogo.png", "png", 50),
|
||||
("apps/desktop/assets/appx/Square44x44Logo.png", "png", 44),
|
||||
("apps/desktop/assets/appx/Square150x150Logo.png", "png", 150),
|
||||
("apps/desktop/public/apple-touch-icon.png", "png", 1024),
|
||||
("apps/desktop/public/nous-girl.jpg", "jpg_white", 256),
|
||||
("apps/bootstrap-installer/src-tauri/icons/32x32.png", "png", 32),
|
||||
("apps/bootstrap-installer/src-tauri/icons/128x128.png", "png", 128),
|
||||
("apps/bootstrap-installer/src-tauri/icons/128x128@2x.png", "png", 256),
|
||||
("apps/bootstrap-installer/src-tauri/icons/icon.ico", "ico", [16, 32, 64, 128, 256]),
|
||||
("apps/bootstrap-installer/src-tauri/icons/icon.icns", "icns", None),
|
||||
("apps/bootstrap-installer/public/nous-girl.jpg", "jpg_white", 256),
|
||||
("website/static/img/logo.png", "logo", None),
|
||||
("website/static/img/nous-logo.png", "png_white", 150),
|
||||
("website/static/img/favicon-16x16.png", "png", 16),
|
||||
("website/static/img/favicon-32x32.png", "png", 32),
|
||||
("website/static/img/apple-touch-icon.png", "png", 180),
|
||||
("website/static/img/favicon.ico", "ico", [16, 32, 48]),
|
||||
("website/static/img/favicon.svg", "svg_copy", None),
|
||||
("web/public/favicon.ico", "ico", [16, 32, 48]),
|
||||
]
|
||||
|
||||
|
||||
def ensure_master(check: bool = False) -> Path:
|
||||
if not MASTER.exists():
|
||||
if check:
|
||||
sys.exit(
|
||||
f"[check] {MASTER.relative_to(ROOT)} is missing — "
|
||||
"cannot verify generated icons without a master."
|
||||
)
|
||||
MASTER.write_text(PLACEHOLDER, encoding="utf-8")
|
||||
print(f"[master] wrote placeholder: {MASTER.relative_to(ROOT)}")
|
||||
return MASTER
|
||||
|
||||
|
||||
def render(size: int, *, background: str | None = None) -> Image.Image:
|
||||
"""Render the master to an RGBA PNG of `size`x`size`."""
|
||||
data = resvg_py.svg_to_bytes(
|
||||
svg_path=str(MASTER), width=size, height=size, background=background
|
||||
)
|
||||
return Image.open(io.BytesIO(data)).convert("RGBA")
|
||||
|
||||
|
||||
def paste_centered(canvas: Image.Image, img: Image.Image) -> None:
|
||||
x = (canvas.width - img.width) // 2
|
||||
y = (canvas.height - img.height) // 2
|
||||
canvas.alpha_composite(img, (x, y))
|
||||
|
||||
|
||||
def build_logo_image() -> Image.Image:
|
||||
"""1772x1799: black frame (~14-17px) + white interior + art at ~1.7x.
|
||||
|
||||
Measured from the real logo.png: border L/R 14, T 17, B 16; white
|
||||
interior spans (15,18)..(1758,1784) = 1743x1766.
|
||||
"""
|
||||
from PIL import ImageDraw
|
||||
|
||||
W, H = 1772, 1799
|
||||
art_w, art_h = 1743, 1766
|
||||
border = 14
|
||||
top, bottom = 17, 16
|
||||
canvas = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||||
|
||||
draw = ImageDraw.Draw(canvas)
|
||||
draw.rounded_rectangle((0, 0, W - 1, H - 1), radius=2, fill=(0, 0, 0, 255))
|
||||
draw.rectangle(
|
||||
(border, top, W - 1 - border, H - 1 - bottom),
|
||||
fill=(255, 255, 255, 255),
|
||||
)
|
||||
|
||||
# Art: render the master square at ~1.7x, tiny vertical stretch to fill.
|
||||
art = render(art_w).resize((art_w, art_h), Image.LANCZOS)
|
||||
canvas.alpha_composite(art, (border, top))
|
||||
return canvas
|
||||
|
||||
|
||||
def target_bytes(kind: str, arg: object) -> bytes:
|
||||
"""Produce the exact bytes for one target. Shared by write + check."""
|
||||
if kind == "svg_copy":
|
||||
return MASTER.read_bytes()
|
||||
|
||||
buf = io.BytesIO()
|
||||
if kind == "png":
|
||||
render(arg).save(buf, "PNG")
|
||||
elif kind == "png_white":
|
||||
render(arg, background="#ffffff").save(buf, "PNG")
|
||||
elif kind == "jpg_white":
|
||||
# JPEG has no alpha: flatten the squircle onto white.
|
||||
render(arg, background="#ffffff").convert("RGB").save(buf, "JPEG", quality=90)
|
||||
elif kind == "ico":
|
||||
img = render(max(arg))
|
||||
img.save(buf, format="ICO", sizes=[(s, s) for s in arg])
|
||||
elif kind == "icns":
|
||||
img = render(1024)
|
||||
frames = [img.resize((s, s), Image.LANCZOS) for s in (16, 32, 64, 128, 256, 512, 1024)]
|
||||
img.save(buf, format="ICNS", append_images=frames[1:])
|
||||
elif kind == "wide":
|
||||
w, h = arg
|
||||
canvas = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
paste_centered(canvas, render(100))
|
||||
canvas.save(buf, "PNG")
|
||||
elif kind == "logo":
|
||||
build_logo_image().save(buf, "PNG")
|
||||
else:
|
||||
raise ValueError(f"unknown kind {kind!r}")
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def cmd_write() -> None:
|
||||
ensure_master()
|
||||
written = 0
|
||||
for rel, kind, arg in TARGETS:
|
||||
path = ROOT / rel
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
path.write_bytes(target_bytes(kind, arg))
|
||||
written += 1
|
||||
except Exception as exc: # noqa: BLE001 - report and keep going
|
||||
print(f" !! {rel}: FAILED ({exc})")
|
||||
print(f"[ok] wrote {written}/{len(TARGETS)} files from {MASTER.name}")
|
||||
|
||||
print("\n[verify]")
|
||||
for rel, kind, arg in TARGETS:
|
||||
path = ROOT / rel
|
||||
try:
|
||||
if kind == "svg_copy":
|
||||
print(f" {rel}: {path.stat().st_size} bytes SVG")
|
||||
continue
|
||||
im = Image.open(path)
|
||||
if kind == "ico":
|
||||
sizes = []
|
||||
try:
|
||||
for i in range(im.n_frames):
|
||||
im.seek(i)
|
||||
sizes.append(im.size)
|
||||
except Exception:
|
||||
sizes = [im.size]
|
||||
print(f" {rel}: ICO {sorted(set(sizes))}")
|
||||
elif kind == "icns":
|
||||
print(f" {rel}: ICNS {im.size} (container)")
|
||||
else:
|
||||
print(f" {rel}: {im.format} {im.size}")
|
||||
except Exception as exc:
|
||||
print(f" {rel}: VERIFY FAILED ({exc})")
|
||||
|
||||
|
||||
def cmd_check() -> int:
|
||||
ensure_master(check=True)
|
||||
drifted: list[str] = []
|
||||
for rel, kind, arg in TARGETS:
|
||||
path = ROOT / rel
|
||||
if not path.exists():
|
||||
drifted.append(f"{rel}: MISSING (expected generated file)")
|
||||
continue
|
||||
try:
|
||||
expected = target_bytes(kind, arg)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
drifted.append(f"{rel}: REGENERATE FAILED ({exc})")
|
||||
continue
|
||||
actual = path.read_bytes()
|
||||
if actual != expected:
|
||||
drifted.append(f"{rel}: drift ({len(actual)} bytes on disk vs {len(expected)} generated)")
|
||||
|
||||
if not drifted:
|
||||
print(f"[ok] all {len(TARGETS)} generated icons match {MASTER.name}")
|
||||
return 0
|
||||
|
||||
print(f"[check] {len(drifted)} file(s) out of sync with {MASTER.name}:")
|
||||
for line in drifted:
|
||||
print(f" - {line}")
|
||||
print(
|
||||
"\nFix: run `.venv/Scripts/python.exe scripts/generate_icons.py` "
|
||||
"and commit the regenerated files."
|
||||
)
|
||||
return 1
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if "--check" in sys.argv:
|
||||
sys.exit(cmd_check())
|
||||
cmd_write()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
246
uv.lock
generated
246
uv.lock
generated
@@ -12,105 +12,105 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for
|
||||
exclude-newer-span = "P14D"
|
||||
|
||||
[options.exclude-newer-package]
|
||||
agent-client-protocol = false
|
||||
ai-edge-litert = false
|
||||
aiohttp = false
|
||||
aiohttp-socks = false
|
||||
aiosqlite = false
|
||||
alibabacloud-dingtalk = false
|
||||
anthropic = false
|
||||
asyncpg = false
|
||||
azure-identity = false
|
||||
boto3 = false
|
||||
brotlicffi = false
|
||||
certifi = false
|
||||
concurrent-log-handler = false
|
||||
croniter = false
|
||||
cryptography = false
|
||||
daytona = false
|
||||
debugpy = false
|
||||
defusedxml = false
|
||||
dingtalk-stream = false
|
||||
discord-py = false
|
||||
edge-tts = false
|
||||
elevenlabs = false
|
||||
exa-py = false
|
||||
fal-client = false
|
||||
fastapi = false
|
||||
faster-whisper = false
|
||||
fire = false
|
||||
firecrawl-anydoc = false
|
||||
firecrawl-py = false
|
||||
google-api-python-client = false
|
||||
setuptools = false
|
||||
modal = false
|
||||
pyyaml = false
|
||||
maturin = false
|
||||
numpy = false
|
||||
pvporcupine = false
|
||||
google-auth = false
|
||||
google-auth-httplib2 = false
|
||||
google-auth-oauthlib = false
|
||||
aiohttp = false
|
||||
starlette = false
|
||||
mistralai = false
|
||||
firecrawl-py = false
|
||||
parallel-web = false
|
||||
markdown = false
|
||||
opentelemetry-sdk = false
|
||||
prompt-toolkit = false
|
||||
fastapi = false
|
||||
anthropic = false
|
||||
pathspec = false
|
||||
openwakeword = false
|
||||
defusedxml = false
|
||||
ruff = false
|
||||
exa-py = false
|
||||
mcp = false
|
||||
python-olm = false
|
||||
wheel = false
|
||||
websockets = false
|
||||
pytest = false
|
||||
pillow = false
|
||||
setuptools-rust = false
|
||||
tenacity = false
|
||||
pyjwt = false
|
||||
slack-bolt = false
|
||||
fal-client = false
|
||||
asyncpg = false
|
||||
boto3 = false
|
||||
ruamel-yaml = false
|
||||
sherpa-onnx = false
|
||||
huggingface-hub = false
|
||||
pytest-asyncio = false
|
||||
google-cloud-pubsub = false
|
||||
h2 = false
|
||||
hindsight-client = false
|
||||
slack-sdk = false
|
||||
discord-py = false
|
||||
honcho-ai = false
|
||||
faster-whisper = false
|
||||
certifi = false
|
||||
debugpy = false
|
||||
packaging = false
|
||||
rich = false
|
||||
supermemory = false
|
||||
unpaddedbase64 = false
|
||||
h2 = false
|
||||
youtube-transcript-api = false
|
||||
edge-tts = false
|
||||
microsoft-teams-apps = false
|
||||
aiohttp-socks = false
|
||||
ai-edge-litert = false
|
||||
onnxruntime = false
|
||||
croniter = false
|
||||
opentelemetry-exporter-otlp-proto-http = false
|
||||
azure-identity = false
|
||||
lark-oapi = false
|
||||
sentencepiece = false
|
||||
firecrawl-anydoc = false
|
||||
google-api-python-client = false
|
||||
brotlicffi = false
|
||||
mem0ai = false
|
||||
qrcode = false
|
||||
vercel = false
|
||||
aiosqlite = false
|
||||
tzdata = false
|
||||
python-dotenv = false
|
||||
daytona = false
|
||||
uvicorn = false
|
||||
nemo-relay = false
|
||||
requests = false
|
||||
httplib2 = false
|
||||
httpx = false
|
||||
httpx2 = false
|
||||
huggingface_hub = false
|
||||
jinja2 = false
|
||||
lark-oapi = false
|
||||
markdown = false
|
||||
maturin = false
|
||||
mautrix = false
|
||||
mcp = false
|
||||
mem0ai = false
|
||||
microsoft-teams-apps = false
|
||||
mistralai = false
|
||||
modal = false
|
||||
nemo-relay = false
|
||||
numpy = false
|
||||
onnxruntime = false
|
||||
openai = false
|
||||
opentelemetry-exporter-otlp-proto-http = false
|
||||
opentelemetry-sdk = false
|
||||
openwakeword = false
|
||||
packaging = false
|
||||
parallel-web = false
|
||||
pathspec = false
|
||||
pillow = false
|
||||
prompt-toolkit = false
|
||||
psutil = false
|
||||
pvporcupine = false
|
||||
pyasn1 = false
|
||||
pydantic = false
|
||||
pyjwt = false
|
||||
pytest = false
|
||||
pytest-asyncio = false
|
||||
python-dotenv = false
|
||||
python-multipart = false
|
||||
python-olm = false
|
||||
python-telegram-bot = false
|
||||
pyyaml = false
|
||||
qrcode = false
|
||||
requests = false
|
||||
rich = false
|
||||
ruamel-yaml = false
|
||||
ruff = false
|
||||
sentencepiece = false
|
||||
setuptools = false
|
||||
setuptools-rust = false
|
||||
sherpa-onnx = false
|
||||
slack-bolt = false
|
||||
slack-sdk = false
|
||||
snowballstemmer = false
|
||||
sounddevice = false
|
||||
starlette = false
|
||||
supermemory = false
|
||||
tenacity = false
|
||||
concurrent-log-handler = false
|
||||
cryptography = false
|
||||
pydantic = false
|
||||
agent-client-protocol = false
|
||||
fire = false
|
||||
httpx2 = false
|
||||
google-auth-oauthlib = false
|
||||
python-multipart = false
|
||||
ty = false
|
||||
tzdata = false
|
||||
unpaddedbase64 = false
|
||||
uvicorn = false
|
||||
vercel = false
|
||||
websockets = false
|
||||
wheel = false
|
||||
youtube-transcript-api = false
|
||||
openai = false
|
||||
mautrix = false
|
||||
google-auth-httplib2 = false
|
||||
python-telegram-bot = false
|
||||
psutil = false
|
||||
jinja2 = false
|
||||
hindsight-client = false
|
||||
sounddevice = false
|
||||
alibabacloud-dingtalk = false
|
||||
dingtalk-stream = false
|
||||
|
||||
[manifest]
|
||||
overrides = [
|
||||
@@ -1805,6 +1805,7 @@ dev = [
|
||||
{ name = "mcp" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "resvg-py" },
|
||||
{ name = "ruff" },
|
||||
{ name = "setuptools" },
|
||||
{ name = "starlette" },
|
||||
@@ -2121,6 +2122,7 @@ requires-dist = [
|
||||
{ name = "qrcode", marker = "extra == 'feishu'", specifier = "==7.4.2" },
|
||||
{ name = "qrcode", marker = "extra == 'messaging'", specifier = "==7.4.2" },
|
||||
{ name = "requests", specifier = "==2.33.0" },
|
||||
{ name = "resvg-py", marker = "extra == 'dev'", specifier = "==0.4.0" },
|
||||
{ name = "rich", specifier = "==14.3.3" },
|
||||
{ name = "ruamel-yaml", specifier = "==0.18.17" },
|
||||
{ name = "ruff", marker = "extra == 'dev'", specifier = "==0.15.10" },
|
||||
@@ -4046,6 +4048,56 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resvg-py"
|
||||
version = "0.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2e/4e/26c3b88f8d2c2ef96c2d9bf48823f484a91275011c58cbeddbc98b32a0cd/resvg_py-0.4.0.tar.gz", hash = "sha256:7b93183f2179bb23411e8451dc6201ad3bceed9b7e35dca2107faf6d8b157c43", size = 1747412, upload-time = "2026-08-12T16:01:33.882Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/49/df4bd6106527281ef72b3916cb949ca760e170b7a01b84a36ecf1c85eac5/resvg_py-0.4.0-cp310-abi3-android_24_arm64_v8a.whl", hash = "sha256:7302bb18376c27b055f692c97b9e73f716bfb0e628715b8eb49b78bdf8489e8b", size = 1307910, upload-time = "2026-08-12T16:00:24.957Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/74/ef6cfea97f5774dfe3dc189134932e43bf6c0f0f34036145e847e97be7b9/resvg_py-0.4.0-cp310-abi3-android_24_x86_64.whl", hash = "sha256:22dc18024ec4b3dbeec4c144c299a3ea829726ca1ff666b1a0bb79c674c92ffd", size = 1313463, upload-time = "2026-08-12T16:00:26.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/ba/46e9eb4da8b85b4a3615025414022773d0b65abf886be8fc69e8b0e7ab31/resvg_py-0.4.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:facd4639edf0cbd800adee3c0f20583331f47f1bf5921953c8564c74081b79bb", size = 1202090, upload-time = "2026-08-12T16:00:27.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/b1/0456a51ee69d40ffdbd49f4a953b7eb93f09eabbd64c1ff4124464270fb6/resvg_py-0.4.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:663f1e77fef38354afa28214cbe2f9512a317b66aae1b5571624f6a9c820f070", size = 1148361, upload-time = "2026-08-12T16:00:28.863Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/e1/49f08214fa4df8b24e009c70b373067d702d5b5f9cb5b03757e6b0ca5f65/resvg_py-0.4.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b1675fef0c5c28e3981082d51e7402bc7d47358b75e8e65f8be1f533d891b15", size = 1344871, upload-time = "2026-08-12T16:00:29.921Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/e3/2fa8f7373d9fc08ee296522d553e72bf18d41f0813be6a6b12d637d7e711/resvg_py-0.4.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0da2aa6e580ff6d9a984da96faeab3e08d7e5328a0e0a93a9eb1563e5143ab1d", size = 1268440, upload-time = "2026-08-12T16:00:31.406Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/dc/e76e0b870bcb543ed109dfba1893757d1ef690150c3a57928720c244f14f/resvg_py-0.4.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47b8b3e65d8abb48bdd3a4a1442abd1b7ba29a3e2783a7b772fe118c10b5f994", size = 1443392, upload-time = "2026-08-12T16:00:32.483Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/c5/4a0f0e954faf36e31f7abab2420ef82c7b0ff3c7165e5772403f6a593937/resvg_py-0.4.0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055dc6e2da744af6cc977f78cbf8ac1cdb544ef1fc9f638dd3da86100ff6f04e", size = 1398857, upload-time = "2026-08-12T16:00:33.713Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/99/058b1188f980116787b9c189f1bc15f4000b8148665f22fbd4c619d62ffb/resvg_py-0.4.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa319c50a1cf11ec868faff88b06233cc4e6959251657cbc809a90e459aabb05", size = 1345682, upload-time = "2026-08-12T16:00:34.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/45/5f68775c5e9d603d417f1753fe076a44b4cfe4774bf7b31911671ba56e13/resvg_py-0.4.0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7675c10e7fcb282186b9e167ee29d4fbfa75af17ba3892df185fd387c6216878", size = 1426219, upload-time = "2026-08-12T16:00:36.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/eb/e8e824c32c9257b70745a160569f56fa964c75df55f45ecf4e8e8e66f3dd/resvg_py-0.4.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c50ed47c2d16cd8cee9f2692523f501d24b431a0ec654ad9137c66805738d564", size = 1523681, upload-time = "2026-08-12T16:00:37.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/54/9eeda766ac87452524d7f6b2bccef8b0e245277e95c9501280aa7df61fc6/resvg_py-0.4.0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:4c6456670ea732611e5ff1019544f09e1f09fa0e90a6fc35b4e96b3c22943b4c", size = 1545125, upload-time = "2026-08-12T16:00:38.949Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/e9/04c66e1fbd7fb75e2c28e504652632ae2d8d1880c96d52b9811163b13a8b/resvg_py-0.4.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:74c1ceea6cfa29b79992834aa3a238600d399f00aeb5df5d02289fd1f4560b96", size = 1588426, upload-time = "2026-08-12T16:00:40.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/5f/6b5a57f796b83a45b97d8f6ebdf0ac7e200e8d33851a48ddf9a5eb07e46f/resvg_py-0.4.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3274d9e168b22dc5c3241619965a64c2d1451685871002329b707b2aeb397b9b", size = 1562330, upload-time = "2026-08-12T16:00:41.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/57/5863b4d6a257f9033f1d187fd921553c4ed2e7f0e71c0d4d0ac21f48b82c/resvg_py-0.4.0-cp310-abi3-win32.whl", hash = "sha256:fee3af4e045049a7da46e7e40ca3cd97e5ea2e5dc66aa9f9769d73d69d300904", size = 1125848, upload-time = "2026-08-12T16:00:42.306Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/7f/b8f825cab25a132c0119b5ad2110dd0dfa6e7e110afae3affb6cfb3b5a0e/resvg_py-0.4.0-cp310-abi3-win_amd64.whl", hash = "sha256:e889d57d51ac913c23e8d1843a29f0356a473519983959b34c6177698e4a3aef", size = 1144555, upload-time = "2026-08-12T16:00:43.574Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/80/ad5b1afeea46401c36c6a016dba7e8b05d0a38e7a1a2ddf3129416bc05d1/resvg_py-0.4.0-cp310-abi3-win_arm64.whl", hash = "sha256:81b837f1915b352aa9b2663730331c7e7c675880b5fdad341d14046ba9a3b4af", size = 1063229, upload-time = "2026-08-12T16:00:44.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/c5/4b82e20ba90d310bee2657ee283cf3d7e3e59b42f28b42343c5049ec2485/resvg_py-0.4.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:f28aef8ebb639a668402c8be3ca4b818f2a2b105a509682a231e0c95d3eaa2bf", size = 1201497, upload-time = "2026-08-12T16:01:03.949Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/7a/00ee34bafff95fea7e8858ea7ceedee797d3ee7512a56e7673262dfd9057/resvg_py-0.4.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:f48e42cfea42aea01edb83125499e17059ef3539b1f6ab950d4d00480ad7011b", size = 1147730, upload-time = "2026-08-12T16:01:05.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/a9/c22f254b165b8f7a7b36e494d4e60f75ff93c0eceeeab199c6d7f836f6ff/resvg_py-0.4.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bff774a5cf4ff0d5554bfb785d864cb38fa4531759672527af13e50bd6065c88", size = 1344020, upload-time = "2026-08-12T16:01:06.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/5d/152241a069573f389a60aeb07757fe256ec2f0aa91936b69d352bb0148e0/resvg_py-0.4.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec75269c60761d4d2cccb8f80649a2ee9015806bb3b339421ae71b22e3a3c8d8", size = 1267463, upload-time = "2026-08-12T16:01:07.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/7a/05a223f6e7a7535c234728b52138a35711831e02a4982533bb404917bb98/resvg_py-0.4.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:830d87b2a512c0355219e4deff5086da6299e01591fe068aeaa5d1a06c7b2450", size = 1441631, upload-time = "2026-08-12T16:01:08.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/35/930ee65c2a1f7fe0ec17d4ba2707efddfd770b1f5769f78c3cdb5a8b07f0/resvg_py-0.4.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fbd0846f594d208f8b28df5adfe0cf5a1d2a0e365d79fa9abd694a7b0e16fe9", size = 1398417, upload-time = "2026-08-12T16:01:09.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/af/b04b9bbc8195f5c81d4e79fc14a69acd4da9ef38dbb496d42d86cd4c8b71/resvg_py-0.4.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586e6ce8e6d368e34e00e705daca3780d506334432940a5fb3695c4e3215cbef", size = 1344689, upload-time = "2026-08-12T16:01:11.095Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/d4/f2a15ac2c4324cab29234e6da98fe54c2c2229f5e55a2fda6fa0e502caee/resvg_py-0.4.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4256a7283bf827442dab20b3614dd9f98c4faf9321389d68478f51f81940d278", size = 1426312, upload-time = "2026-08-12T16:01:12.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/6a/9068dbffee179f1b378ec5b4906edbc5c8feb6afcd5215886640d445b676/resvg_py-0.4.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:407de28dc272b48942fd7820f3dc9c64266ac7a32603763114767196a3ca8a27", size = 1522267, upload-time = "2026-08-12T16:01:13.546Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/2e/6c4662168aad28b13398da4d1292c86aa34e4fe814dabf5eeb00bb3229b7/resvg_py-0.4.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:3614654205efbf516f3c5eaab619f931dce6a08bd41beb8bb9a786ff15bcc1c3", size = 1544578, upload-time = "2026-08-12T16:01:15.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/93/1b9b50f3a72d36b2f8d454874fef1707c60495fa9a605a518feba2da122e/resvg_py-0.4.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:ea9f555f0b5212c7c773b6fdb0597d5ab95983213d93c1753b1f7929c371d542", size = 1588383, upload-time = "2026-08-12T16:01:16.457Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/a8/0681fe9835bc7c788b237d39c5172ef51ce4efb3f78ee91fef93116d6e09/resvg_py-0.4.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:a811975def9a8a643170ae2271b67232a99a438d74d08a6e7e0665f6eb753159", size = 1561365, upload-time = "2026-08-12T16:01:17.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/05/3c327408a93ccb76cadccb02eee5afdd1b89988bd5dffe0c4bc53f47a1b9/resvg_py-0.4.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:dcd45cb27bbe955cf9b3c7b6d3a4d2a4ba43b9761f7248daba04f42d3f88d49f", size = 1125887, upload-time = "2026-08-12T16:01:18.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/f6/6f1e358cb266ab9867733d905223c0f419d7db63e980576ab0e9853b466d/resvg_py-0.4.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:a83beeebc7ed1b707bcebb4113c247ecb944551203c910aa2b1709e4498b68d3", size = 1143340, upload-time = "2026-08-12T16:01:20.074Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/cd/0ee87a5ddc6f320dc827bd5e2749dfa081e8c8ddda22ea7bbb1f561e1716/resvg_py-0.4.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:25f81ea0c2b0a5edf90eec5b662933d97b89abeb62019bd5eaee37aca327debe", size = 1061705, upload-time = "2026-08-12T16:01:21.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/e9/9c90d2488f9e579a2ffc743704bc35223a451c48bae912e4b527475a3756/resvg_py-0.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d389cc4b21ca81113b8fe828a630d66f11ffa1dc559af2ef41798de88a443c4a", size = 1345594, upload-time = "2026-08-12T16:01:22.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/0b/f746632146d954626e1fecdc3f4028f91586a4f73a3d5b6339f5563e1000/resvg_py-0.4.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5f7c60ac7c43eb46645e28f5f9a64b5795ce59be362d719119d843808761969", size = 1268619, upload-time = "2026-08-12T16:01:23.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/41/b70597b88597343f522600a911c13cb9a20c5f8a8e415f175d1ac4f2b85a/resvg_py-0.4.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:82425e89f8ff18d077f7dfb92bc86b469e6671ac48aebd5736f9b01c77ee0808", size = 1444001, upload-time = "2026-08-12T16:01:24.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/7f/6741146cd45e421fdff04f736e0cb2b79c63845b717654fe636d9193c1e4/resvg_py-0.4.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba433618a078685e7d7058a9f1db631a5608666e5d9d558a94f5292a5f561548", size = 1399164, upload-time = "2026-08-12T16:01:25.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/e1/49eec6a6faadc9f5ac5dc9efc0cdfc82028ae92e156b964a9c98b5fbf8c5/resvg_py-0.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:006123008d602f20948ae4048b4ace5665bf71d48333d4d3f9e6fac21d7fd277", size = 1345949, upload-time = "2026-08-12T16:01:26.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/a6/bcc4db3e6e22a2e6351b35be9ad4e33db968ee2a4ba173597151fb538a53/resvg_py-0.4.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e414b09cc4ebf418b7ceb52ffb777296f6f9f62ecf85b8beb36e9a70dc709f4f", size = 1426578, upload-time = "2026-08-12T16:01:27.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/dc/e05fbb2861a036926d126024cb888cff4892831689d981e00a742d631bfd/resvg_py-0.4.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:69ffc6fd35ac0db183331d8a401d2ed60ba9edceb44ecda1757c3a455f9af3bd", size = 1523976, upload-time = "2026-08-12T16:01:29.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/5e/2c42980324e999b94cc689c7445848715cfe1b8d8d7af194bea1d166dd2e/resvg_py-0.4.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0647b6bea989da4f282ebc2b06fadfdc29045e35a5761810aa876bb1ce520146", size = 1545404, upload-time = "2026-08-12T16:01:30.201Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/b4/79365305c62797adc79d614d0db22d5773500eab9d0634cf7794e6a57a46/resvg_py-0.4.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:eba4f2393869392e0e5d86820ab246427899b1098f2f71c83f4f83ac3f0bc422", size = 1588538, upload-time = "2026-08-12T16:01:31.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/6e/8854bae4907280edec643c0bb2d125acbd41f24ce04383ac0bf3e668d35e/resvg_py-0.4.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:19ce20db44718eef646401039a39d9ec72bda1298ca8401c8a913ffaa6573fc4", size = 1562622, upload-time = "2026-08-12T16:01:32.559Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "14.3.3"
|
||||
@@ -4267,7 +4319,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 = [
|
||||
@@ -4322,7 +4374,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 = [
|
||||
@@ -4970,11 +5022,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 = [
|
||||
|
||||
Reference in New Issue
Block a user