refactor: restructure mytoolkit with new subcommands and md-to-pdf improvements

- Add new subcommands: convert, preflight, server, templates, webpage
- Migrate config from bin/config.json to ~/.config/mytoolkit
- Fix expand_bookmarks to modify writer objects instead of reader
- Improve md_to_pdf with CJK bookmark support
- Update README and project metadata
This commit is contained in:
Zhengshou Lai
2026-05-04 17:19:32 +08:00
parent 38328c28d0
commit 3e4cf618a8
20 changed files with 1846 additions and 214 deletions
-3
View File
@@ -11,6 +11,3 @@ build/
.venv/
.pytest_cache/
.mypy_cache/
# Config contains sensitive data (API keys)
bin/config.json
+61 -106
View File
@@ -1,134 +1,89 @@
# MyCLI
# mytoolkit
Personal CLI toolkit - unified interface for various utility scripts.
Personal CLI toolkit unified entry point for everyday file conversion, format wrangling, and project chores.
## Installation
## Install
```bash
make install
make install # builds + symlinks ~/.local/bin/mytoolkit
mytoolkit --version
mytoolkit templates register ~/workspace/templates # one-time, points at template root
```
## Commands
## Top-level commands
```bash
mytoolkit --help
# PDF utilities
mytoolkit pdf compress *.pdf
mytoolkit pdf crop *.pdf
mytoolkit pdf to-tiff *.pdf
# Image utilities
mytoolkit image eps-to-pdf *.eps
mytoolkit image jpg-to-pdf *.jpg -o output.pdf
mytoolkit image tiff-to-pdf *.tiff
mytoolkit image tiff-compress *.tiff
# LaTeX utilities
mytoolkit latex compile *.tex
mytoolkit latex count document.tex
# Video utilities
mytoolkit video avi-to-mp4 *.avi
# BibTeX utilities
mytoolkit bib to-markdown *.bib
mytoolkit bib cv-update
# Utilities
mytoolkit utils albany-cleanup
# Environment & Navigation
mytoolkit env list # List configured paths
mytoolkit env goto research # Print path for cd
mytoolkit env cd apaam # Start shell in path
mytoolkit env status # Show environment status
cd $(mytoolkit env goto study) # Use in scripts
# SSH Connections
mytoolkit ssh list # List configured hosts
mytoolkit ssh connect tianhe # SSH to host
mytoolkit ssh connect starlight
mytoolkit ssh tunnel tianhe # Create port tunnel
mytoolkit ssh cmd tianhe "ls" # Run remote command
# Git Proxy Management
# Git Proxy Management
mytoolkit git proxy status # Show proxy status
mytoolkit git proxy set fastgithub # Set proxy
mytoolkit git proxy set pandafan
mytoolkit git proxy unset # Unset proxy
mytoolkit git proxy list # List available proxies
# Self Management
mytoolkit self update # Update mytoolkit
mytoolkit self uninstall # Uninstall mytoolkit
mytoolkit self info # Show installation info
# Configuration (config.json) - 增删改查
# 方式1: 交互式向导(推荐,无需记忆命令)
mytoolkit wizard # 启动交互式配置向导
# 方式2: 命令行直接操作
mytoolkit config show # Show full config
mytoolkit config list # List top-level sections
mytoolkit config get paths # Get paths section
mytoolkit config get paths.apaam # Get specific path
mytoolkit config get api_keys.deepseek # Get API key
mytoolkit config set paths.new ~/Documents/new # Set simple value
mytoolkit config set api_keys.newkey abc123 # Set new API key
mytoolkit config set --json ssh_hosts.new '{"host":"user@host","port":22}'
mytoolkit config delete paths.old # Delete a key
mytoolkit config delete api_keys.old -y # Delete without confirmation
mytoolkit config paths # List all paths with existence check
mytoolkit config where # Show config file location
mytoolkit config edit # Edit config in $EDITOR
mytoolkit config init # Create config from template
# Environment Variables (API Keys)
mytoolkit env status # Show paths, API keys, Feishu status
mytoolkit env export # Print all export statements
mytoolkit env export deepseek # Export specific API key
mytoolkit env export qwen
mytoolkit env export feishu # Export all Feishu apps
mytoolkit env export claude # Export specific Feishu app
eval "$(mytoolkit env export)" # Load all in current shell
mytoolkit env init # Print shell init snippet
```
## Configuration
| Command | What it does |
|---------|--------------|
| `convert` | Universal format conversion (md/img/pdf/eps/avi/bib → pdf/docx/tiff/mp4/md). Auto-detects from extensions. |
| `pdf` | `compress` / `crop` / `merge` / `bookmark` / `to-tiff` |
| `image` | `compress` / `eps-fix` / `eps-to-pdf` / `jpg-to-pdf` / `tiff-to-pdf` / `tiff-compress` / `generate` (Doubao/Seedream) |
| `latex` | `compile` / `count` |
| `bib` | `to-markdown` / `cv-update` |
| `video` | `avi-to-mp4` |
| `webpage` | Docusaurus build/list/rebuild |
| `mail` | IMAP/SMTP client (read/draft/forward/reply, no auto-send) |
| `templates` | Register and inspect external template root |
| `env` | Manage stored vars (api keys, paths, feishu app credentials) |
| `ssh` | `list` / `connect` / `tunnel` / `cmd` |
| `git` | `proxy` (set/unset/list/status) |
| `server` | Manage dev/serve servers |
| `preflight` | Pre-flight checks for work-order execution |
| `utils` | `albany-cleanup` |
| `update` | Reinstall from local repo |
| `uninstall` | Uninstall |
All settings are stored in `bin/config.json`:
- `paths`: Quick navigation paths
- `ssh_hosts`: SSH connection settings
- `git_proxies`: Git proxy URLs
- `openfoam`: OpenFOAM mount settings
- `api_keys`: API keys (deepseek, qwen)
- `feishu_apps`: Feishu app credentials
For any subcommand: `mytoolkit <cmd> --help`.
### Security Note
## Templates
`config.json` contains sensitive information (API keys) and is **gitignored**.
The repository includes `config.json.example` as a template.
`mytoolkit convert -t <name>` reads templates from an external directory you register (templates are not bundled). Layout:
### Setup
```
<root>/md-to-pdf/<name>/template.typ
<root>/md-to-docx/<name>/template.docx
```
1. Copy the example config:
```bash
cp bin/config.json.example bin/config.json
mytoolkit templates register /path/to/templates
mytoolkit templates show # lists each template with its absolute path
```
2. Edit with your actual API keys:
Registry is stored at `~/.config/mytoolkit/templates.json`.
## User config
All user-level config lives under `~/.config/mytoolkit/`:
| File | Owner | Purpose |
|------|-------|---------|
| `env.json` | `mytoolkit env` | API keys, paths, feishu credentials (flat key/value) |
| `mail.json` | `mytoolkit mail` | IMAP/SMTP server settings |
| `templates.json` | `mytoolkit templates` | Pointer to external template root |
Manage env vars via the CLI — no need to hand-edit:
```bash
mytoolkit config edit
mytoolkit env list # show all (Storage path printed at top, secrets masked)
mytoolkit env get apikey_ark # read one
mytoolkit env set apikey_ark <value> # write one
mytoolkit env set apikey_ark --secret # prompt for value, hidden input
mytoolkit env remove apikey_ark # delete one
mytoolkit env export # print MYCLI_FOO=... export statements
eval "$(mytoolkit env export)" # load into current shell
```
3. Add to your `~/.zshrc` to auto-load:
```bash
eval "$(mytoolkit env export 2>/dev/null)"
```
Legacy `bin/config.json` (in the source dir) is auto-migrated to `~/.config/mytoolkit/env.json` on first run after upgrade.
## Uninstall
```bash
mytoolkit uninstall
# or
make uninstall
```
User config under `~/.config/mytoolkit/` is left in place.
+19 -2
View File
@@ -1,9 +1,21 @@
"""MyCLI main entry point."""
from pathlib import Path
import click
from bin.commands import pdf, image, latex, video, bib, utils, env_cmd, ssh_cmd, git_cmd, mail
from bin.commands import pdf, image, latex, video, bib, utils
from bin.commands.convert import convert_cmd
from bin.commands.env import env_cmd
from bin.commands.templates import templates_cmd
from bin.commands.ssh import ssh_cmd
from bin.commands.git import git_cmd
from bin.commands.server import server_cmd
from bin.commands.webpage import webpage_cmd
from bin.commands.mail import mail
from bin.commands.preflight import preflight_cmd
from bin.commands.self_mgmt import update_cmd, uninstall_cmd, self_cmd
from bin.utils import handle_errors
@click.group()
@@ -14,6 +26,7 @@ def cli():
# Register command groups
cli.add_command(convert_cmd)
cli.add_command(pdf.pdf)
cli.add_command(image.image)
cli.add_command(latex.latex)
@@ -21,9 +34,13 @@ cli.add_command(video.video)
cli.add_command(bib.bib)
cli.add_command(utils.utils)
cli.add_command(env_cmd)
cli.add_command(templates_cmd)
cli.add_command(ssh_cmd)
cli.add_command(git_cmd)
cli.add_command(mail.mail)
cli.add_command(server_cmd)
cli.add_command(webpage_cmd)
cli.add_command(mail)
cli.add_command(preflight_cmd)
cli.add_command(update_cmd)
cli.add_command(uninstall_cmd)
cli.add_command(self_cmd)
+3 -1
View File
@@ -4,6 +4,8 @@ from . import pdf, image, latex, video, bib, utils
from .env import env_cmd
from .ssh import ssh_cmd
from .git import git_cmd
from .server import server_cmd
from .webpage import webpage_cmd
from .self_mgmt import self_cmd, update_cmd, uninstall_cmd
__all__ = ["pdf", "image", "latex", "video", "bib", "utils", "env_cmd", "ssh_cmd", "git_cmd", "self_cmd", "update_cmd", "uninstall_cmd"]
__all__ = ["pdf", "image", "latex", "video", "bib", "utils", "env_cmd", "ssh_cmd", "git_cmd", "server_cmd", "webpage_cmd", "self_cmd", "update_cmd", "uninstall_cmd"]
+206
View File
@@ -0,0 +1,206 @@
"""Universal format conversion."""
from pathlib import Path
import click
from pypdf import PdfReader, PdfWriter
from PIL import Image
from bin.md_to_pdf import build_pdf
from bin import templates_registry
from bin.utils import handle_errors, run_command
@click.command("convert")
@click.argument("inputs", nargs=-1, required=True)
@click.option("-o", "--output", required=True, help="Output file path")
@click.option(
"-t",
"--template",
type=click.Choice(["default", "cv", "textbook", "manual", "review"]),
default="default",
help="Template for md→pdf / md→docx",
)
@click.option(
"-q",
"--quality",
type=click.Choice(["screen", "ebook", "printer", "prepress"]),
default="screen",
help="PDF compression quality",
)
@click.option("-d", "--density", default="300", help="DPI for PDF→TIFF")
@click.option("--reference-doc", help="Reference docx template for md→docx")
@click.option(
"--bookmark-depth",
type=int,
default=1,
help="PDF bookmark expansion depth (md→pdf only). 1=top-level only, 2=expand to second level, etc.",
)
@click.option("--dry-run", "-n", is_flag=True, help="Show what would be done")
@handle_errors
def convert_cmd(inputs, output, template, quality, density, reference_doc, bookmark_depth, dry_run):
"""Universal format conversion. Auto-detects from file extensions.
\b
Examples:
mytoolkit convert doc.md -o doc.pdf
mytoolkit convert doc.md -o doc.pdf -t cv
mytoolkit convert doc.md -o doc.docx
mytoolkit convert doc.md -o doc.docx -t review
mytoolkit convert *.jpg -o album.pdf
mytoolkit convert fig.eps -o fig.pdf
mytoolkit convert scan.tiff -o scan.pdf
mytoolkit convert scan.pdf -o scan.tiff
mytoolkit convert video.avi -o video.mp4
"""
out_path = Path(output)
out_ext = out_path.suffix.lower()
# Resolve input globs
input_paths = []
for pattern in inputs:
p = Path(pattern)
if p.exists():
input_paths.append(p)
else:
# Try glob
matches = list(Path(".").glob(pattern))
input_paths.extend(sorted(matches))
if not input_paths:
click.secho("No input files found", fg="red", err=True)
raise SystemExit(1)
# --- pdf → pdf (compress) ---
if len(input_paths) == 1 and input_paths[0].suffix.lower() == ".pdf" and out_ext == ".pdf":
if dry_run:
click.echo(f"Would compress {input_paths[0]} to {output} (quality={quality})")
return
run_command(
[
"gs",
"-sDEVICE=pdfwrite",
"-dNOPAUSE",
"-dQUIET",
"-dBATCH",
f"-dPDFSETTINGS=/{quality}",
"-dCompatibilityLevel=1.4",
f"-sOutputFile={out_path}",
str(input_paths[0]),
],
check=True,
)
click.secho(f"Compressed PDF: {output}", fg="green")
return
# --- md → docx ---
if all(p.suffix.lower() == ".md" for p in input_paths) and out_ext == ".docx":
ref_doc = reference_doc
if not ref_doc and template != "default":
ref_path = templates_registry.get_md_to_docx_root() / template / "template.docx"
if ref_path.exists():
ref_doc = str(ref_path)
else:
click.secho(f"Docx template not found for '{template}': {ref_path}", fg="red", err=True)
raise SystemExit(1)
if dry_run:
msg = f"Would convert {len(input_paths)} markdown file(s) to {output}"
if ref_doc:
msg += f" (template={ref_doc})"
click.echo(msg)
return
cmd = ["pandoc"] + [str(p) for p in input_paths] + ["-o", str(out_path)]
if ref_doc:
cmd.extend(["--reference-doc", ref_doc])
run_command(cmd, check=True)
click.secho(f"Word document generated: {output}", fg="green")
return
# --- md → pdf ---
if all(p.suffix.lower() == ".md" for p in input_paths) and out_ext == ".pdf":
if template == "review":
click.secho("Template 'review' is only available for md→docx", fg="red", err=True)
raise SystemExit(1)
if dry_run:
click.echo(f"Would convert {len(input_paths)} markdown file(s) to {output}")
return
build_pdf(template, input_paths, out_path, bookmark_depth=bookmark_depth)
click.secho(f"PDF generated: {output}", fg="green")
return
# --- images → pdf ---
img_exts = (".jpg", ".jpeg", ".png", ".tif", ".tiff")
if all(p.suffix.lower() in img_exts for p in input_paths) and out_ext == ".pdf":
if dry_run:
click.echo(f"Would convert {len(input_paths)} image(s) to {output}")
return
_images_to_pdf(input_paths, out_path)
click.secho(f"PDF generated: {output}", fg="green")
return
# --- eps → pdf ---
if len(input_paths) == 1 and input_paths[0].suffix.lower() in (".eps", ".ps") and out_ext == ".pdf":
if dry_run:
click.echo(f"Would convert {input_paths[0]} to {output}")
return
run_command(["epstopdf", str(input_paths[0]), "--outfile", str(out_path)], check=True)
click.secho(f"PDF generated: {output}", fg="green")
return
# --- pdf → tiff ---
if len(input_paths) == 1 and input_paths[0].suffix.lower() == ".pdf" and out_ext in (".tiff", ".tif"):
if dry_run:
click.echo(f"Would convert {input_paths[0]} to {output}")
return
run_command(
["convert", "-density", density, str(input_paths[0]), "-compress", "zip", str(out_path)],
check=False,
)
click.secho(f"TIFF generated: {output}", fg="green")
return
# --- avi → mp4 ---
if len(input_paths) == 1 and input_paths[0].suffix.lower() == ".avi" and out_ext == ".mp4":
if dry_run:
click.echo(f"Would convert {input_paths[0]} to {output}")
return
run_command(
["ffmpeg", "-i", str(input_paths[0]), "-c:v", "libx264", "-c:a", "aac", str(out_path)],
check=False,
)
click.secho(f"MP4 generated: {output}", fg="green")
return
# --- bib → md ---
if len(input_paths) == 1 and input_paths[0].suffix.lower() == ".bib" and out_ext == ".md":
if dry_run:
click.echo(f"Would convert {input_paths[0]} to {output}")
return
content = input_paths[0].read_text()
md_content = f"# Bibliography\n\n```bibtex\n{content}\n```\n"
out_path.write_text(md_content)
click.secho(f"Markdown generated: {output}", fg="green")
return
click.secho(
f"Unsupported conversion: {[p.suffix for p in input_paths]}{out_ext}",
fg="red",
err=True,
)
raise SystemExit(1)
def _images_to_pdf(images: list[Path], output: Path) -> None:
"""Merge images into a single PDF."""
writer = PdfWriter()
for img_path in images:
img = Image.open(img_path)
if img.mode in ("RGBA", "P"):
img = img.convert("RGB")
temp_pdf = img_path.with_suffix(".temp.pdf")
img.save(temp_pdf, "PDF", resolution=150.0)
reader = PdfReader(str(temp_pdf))
writer.add_page(reader.pages[0])
temp_pdf.unlink()
with open(output, "wb") as f:
writer.write(f)
+3 -1
View File
@@ -2,7 +2,7 @@
import click
from bin.config import config
from bin.config import CONFIG_PATH, config
@click.group(name="env")
@@ -14,10 +14,12 @@ def env_cmd():
@env_cmd.command("list")
def env_list():
"""List all vars."""
click.echo(f"Storage: {CONFIG_PATH}")
vars = config.get_all()
if not vars:
click.echo("No vars configured.")
return
click.echo()
for name, value in sorted(vars.items()):
masked = "***" if any(x in name.lower() for x in ["key", "secret", "token", "pass"]) else value
click.echo(f"{name:20} = {masked}")
+53
View File
@@ -1,10 +1,14 @@
"""Image utilities."""
import base64
import os
import subprocess
import urllib.request
from pathlib import Path
import click
from bin.config import config
from bin.utils import handle_errors, run_command
@@ -181,3 +185,52 @@ def compress_images(files, target_mb, max_width, quality, suffix, dry_run):
size_mb = output.stat().st_size / (1024 * 1024)
click.echo(f"Compressed: {img_file} -> {output} ({size_mb:.2f} MB)")
@image.command("generate")
@click.argument("prompt", required=False)
@click.option("-f", "--file", type=click.Path(exists=True), help="从文件读取 prompt")
@click.option("-s", "--size", default="2K", type=click.Choice(["1K", "2K"]), help="图片尺寸")
@click.option("-o", "--output", help="保存路径(默认只输出 URL")
@click.option("--no-watermark", is_flag=True, help="不添加豆包水印")
@click.option("--b64", is_flag=True, help="使用 b64_json 格式获取图片数据")
@handle_errors
def generate_image_cmd(prompt, file, size, output, no_watermark, b64):
"""使用豆包/Seedream 模型生成图片。"""
from openai import OpenAI
if file:
prompt = Path(file).read_text(encoding="utf-8").strip()
elif not prompt:
raise click.UsageError("必须提供 prompt 或使用 -f/--file 从文件读取")
api_key = config.get("apikey_ark")
if not api_key:
click.echo("Error: apikey_ark not set. Run: mytoolkit env set apikey_ark <value>", err=True)
raise click.Abort()
client = OpenAI(
base_url="https://ark.cn-beijing.volces.com/api/v3",
api_key=api_key,
)
resp = client.images.generate(
model="doubao-seedream-5-0-260128",
prompt=prompt,
size=size,
response_format="b64_json" if b64 else "url",
extra_body={"watermark": not no_watermark},
)
if b64:
image_bytes = base64.b64decode(resp.data[0].b64_json)
save_path = output or "generated_image.png"
Path(save_path).write_bytes(image_bytes)
click.echo(os.path.abspath(save_path))
else:
url = resp.data[0].url
if output:
urllib.request.urlretrieve(url, output)
click.echo(os.path.abspath(output))
else:
click.echo(url)
+44 -2
View File
@@ -1,11 +1,13 @@
"""PDF utilities."""
import shutil
from pathlib import Path
import click
from pypdf import PdfReader, PdfWriter
from PIL import Image
from bin.md_to_pdf import expand_bookmarks
from bin.utils import handle_errors, run_command
@@ -24,9 +26,15 @@ def pdf():
default="screen",
help="Compression quality level",
)
@click.option(
"--bookmark-depth",
type=int,
default=1,
help="Expand PDF bookmarks to this depth after compression (1=no expansion).",
)
@click.option("--dry-run", "-n", is_flag=True, help="Show what would be done")
@handle_errors
def compress(files, quality, dry_run):
def compress(files, quality, bookmark_depth, dry_run):
"""Compress PDF files using ghostscript."""
for pattern in files:
for pdf_file in Path(".").glob(pattern):
@@ -53,6 +61,8 @@ def compress(files, quality, dry_run):
click.echo(f"Compressing: {pdf_file}")
run_command(cmd, check=True)
output.replace(pdf_file)
if bookmark_depth > 1:
expand_bookmarks(pdf_file, bookmark_depth)
click.echo(f" Done: {pdf_file}")
@@ -102,9 +112,15 @@ def to_tiff(files, density, dry_run):
@pdf.command("merge")
@click.argument("files", nargs=-1, required=True)
@click.option("--output", "-o", required=True, help="Output PDF file")
@click.option(
"--bookmark-depth",
type=int,
default=1,
help="Expand PDF bookmarks to this depth after merge (1=no expansion).",
)
@click.option("--dry-run", "-n", is_flag=True, help="Show what would be done")
@handle_errors
def merge_pdfs(files, output, dry_run):
def merge_pdfs(files, output, bookmark_depth, dry_run):
"""Merge PDFs and/or images into a single PDF."""
writer = PdfWriter()
processed = []
@@ -144,4 +160,30 @@ def merge_pdfs(files, output, dry_run):
with open(output, "wb") as f:
writer.write(f)
if bookmark_depth > 1:
expand_bookmarks(Path(output), bookmark_depth)
click.echo(f"Merged {len(processed)} files into {output}")
@pdf.command("bookmark")
@click.argument("input_file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
@click.option(
"--depth",
type=int,
required=True,
help="Bookmark expansion depth. 1=top-level only, 2=expand to second level, etc.",
)
@click.option(
"-o",
"--output",
type=click.Path(dir_okay=False, path_type=Path),
help="Output PDF path. Defaults to overwriting the input file.",
)
@handle_errors
def bookmark(input_file, depth, output):
"""Set PDF bookmark outline expansion depth."""
target = output if output else input_file
if output and output.resolve() != input_file.resolve():
shutil.copy2(str(input_file), str(target))
expand_bookmarks(target, depth)
click.echo(f"Bookmarks expanded to depth {depth}: {target}")
+119
View File
@@ -0,0 +1,119 @@
"""Pre-flight check for work-order consistency and system health."""
import json
import os
import re
import sys
from pathlib import Path
import click
@click.command(name="preflight")
def preflight_cmd():
"""Run pre-flight checks before executing work orders."""
issues = []
warnings = []
click.secho("Running pre-flight checks...", fg="cyan")
click.echo("")
# === Check 1: work-order.md vs checkpoint JSON consistency ===
work_order = Path.home() / "workspace/assistant/work-order.md"
if work_order.exists():
content = work_order.read_text()
# Find progress numbers like "进度: X/Y 步骤"
progress_match = re.search(r"进度[:]\s*(\d+)/(\d+)\s*步骤", content)
if progress_match:
wo_current = int(progress_match.group(1))
wo_total = int(progress_match.group(2))
# Find checkpoint files referenced in work-order
cp_match = re.search(r"进度档[:]\s*(\S+)", content)
if cp_match:
cp_path = Path.home() / "workspace" / cp_match.group(1)
if cp_path.exists():
try:
cp = json.loads(cp_path.read_text())
cp_current = cp.get("currentStep", 0)
cp_total = cp.get("totalSteps", 0)
cp_completed = len(cp.get("completedSteps", []))
if wo_current != cp_current or wo_total != cp_total:
issues.append(
f"进度不一致: 工作单 {wo_current}/{wo_total} vs 进度档 {cp_current}/{cp_total}"
)
elif cp_current != cp_completed + 1 and cp_current <= cp_total:
# currentStep should be next step to do (completed + 1)
# unless all completed
pass
except json.JSONDecodeError:
warnings.append(f"进度档 JSON 损坏: {cp_path}")
# Check for duplicate step numbers in step list
step_lines = re.findall(r"-\s*\[[x ]\]\s*\d+\.", content)
step_nums = []
for line in step_lines:
m = re.search(r"(\d+)\.", line)
if m:
step_nums.append(int(m.group(1)))
seen = set()
for num in step_nums:
if num in seen:
issues.append(f"步骤 {num} 在清单中重复出现")
seen.add(num)
# Check for gaps in step numbering
if step_nums:
expected = list(range(1, max(step_nums) + 1))
missing = [n for n in expected if n not in seen]
if missing:
warnings.append(f"步骤编号缺失: {missing}")
else:
warnings.append("工作单文件不存在")
# === Check 2: .zshrc PATH references ===
zshrc = Path.home() / ".zshrc"
if zshrc.exists():
zshrc_content = zshrc.read_text()
# Find paths in PATH exports (lines starting with export PATH=)
for line in zshrc_content.splitlines():
if not line.strip().startswith("export PATH="):
continue
refs = re.findall(r"/Users/\S+", line)
for ref in refs:
full_path = Path(ref).expanduser()
if not full_path.exists():
issues.append(f".zshrc PATH 引用失效: {ref}")
else:
warnings.append(".zshrc 不存在")
# === Check 3: Workspace symlink health ===
workspace = Path.home() / "workspace"
if workspace.exists():
for item in workspace.iterdir():
if item.is_symlink():
if not item.exists():
issues.append(f"符号链接失效: {item.name} -> {os.readlink(item)}")
# === Report ===
click.echo("-" * 50)
if issues:
click.secho(f"发现 {len(issues)} 个问题:", fg="red")
for issue in issues:
click.secho(f" [x] {issue}", fg="red")
else:
click.secho("未发现严重问题", fg="green")
if warnings:
click.secho(f"\n{len(warnings)} 个警告:", fg="yellow")
for w in warnings:
click.secho(f" [!] {w}", fg="yellow")
click.echo("-" * 50)
if issues:
sys.exit(1)
+86 -37
View File
@@ -1,60 +1,110 @@
"""Self-management commands for bin."""
import os
import shutil
import subprocess
import sys
from pathlib import Path
import click
def _get_project_root() -> Path:
"""Get mytoolkit project root."""
# This file is at bin/commands/self_mgmt.py
"""Get mytoolkit project root from source tree."""
return Path(__file__).parent.parent.parent
def _run(cmd: list[str], cwd: Path | None = None, check: bool = True) -> None:
result = subprocess.run(cmd, cwd=str(cwd) if cwd else None, check=False)
if check and result.returncode != 0:
raise click.Exit(result.returncode)
def _has_uv() -> bool:
return shutil.which("uv") is not None
def _install(root: Path) -> None:
venv_toolkit = root / ".venv" / "bin" / "mytoolkit"
user_local = Path.home() / ".local" / "bin" / "mytoolkit"
comp_dir = Path.home() / ".local" / "bin" / "completions"
# 1. Sync venv / install package
click.secho("Installing package…", fg="cyan")
if _has_uv():
_run(["uv", "sync"], cwd=root)
else:
venv_pip = root / ".venv" / "bin" / "pip"
if not venv_pip.exists():
_run([sys.executable, "-m", "venv", str(root / ".venv")])
_run([str(venv_pip), "install", "-e", str(root)], cwd=root)
if not venv_toolkit.exists():
click.secho(f"error: missing {venv_toolkit}", fg="red", err=True)
raise click.Exit(1)
# 2. Symlink to ~/.local/bin
click.secho(f"Linking {user_local}{venv_toolkit}", fg="cyan")
user_local.parent.mkdir(parents=True, exist_ok=True)
if user_local.exists() or user_local.is_symlink():
user_local.unlink()
user_local.symlink_to(venv_toolkit)
# 3. Install completions
comp_dir.mkdir(parents=True, exist_ok=True)
shell = os.environ.get("SHELL", "")
if "zsh" in shell:
comp_file = comp_dir / "_mytoolkit"
result = subprocess.run(
[str(venv_toolkit)],
env={**os.environ, "_MYTOOLKIT_COMPLETE": "zsh_source"},
capture_output=True,
text=True,
)
if result.returncode == 0:
comp_file.write_text(result.stdout)
click.secho(f"Installed zsh completion: {comp_file}", fg="green")
else:
click.secho("Warning: failed to generate zsh completion", fg="yellow")
elif "bash" in shell:
comp_file = comp_dir / "mytoolkit.bash"
result = subprocess.run(
[str(venv_toolkit)],
env={**os.environ, "_MYTOOLKIT_COMPLETE": "bash_source"},
capture_output=True,
text=True,
)
if result.returncode == 0:
comp_file.write_text(result.stdout)
click.secho(f"Installed bash completion: {comp_file}", fg="green")
else:
click.secho("Warning: failed to generate bash completion", fg="yellow")
def _uninstall() -> None:
user_local = Path.home() / ".local" / "bin" / "mytoolkit"
comp_dir = Path.home() / ".local" / "bin" / "completions"
for f in (comp_dir / "_mytoolkit", comp_dir / "mytoolkit.bash", user_local):
if f.exists() or f.is_symlink():
f.unlink()
click.secho(f"Removed {f}", fg="green")
@click.command(name="update")
def update_cmd():
"""Update mytoolkit from the local repository."""
root = _get_project_root()
click.secho("Updating mytoolkit...", fg="cyan")
# Run make install
result = subprocess.run(
["make", "install"],
cwd=str(root),
capture_output=True,
text=True,
)
if result.returncode == 0:
click.secho("mytoolkit updated successfully.", fg="green")
else:
click.secho("Update failed:", fg="red", err=True)
click.echo(result.stderr, err=True)
raise click.Exit(1)
click.secho("Updating mytoolkit…", fg="cyan")
_install(root)
click.secho("mytoolkit updated.", fg="green")
@click.command(name="uninstall")
def uninstall_cmd():
"""Uninstall mytoolkit."""
root = _get_project_root()
click.secho("Uninstalling mytoolkit...", fg="cyan")
result = subprocess.run(
["make", "uninstall"],
cwd=str(root),
capture_output=True,
text=True,
)
if result.returncode == 0:
click.secho("mytoolkit uninstalled.", fg="green")
else:
click.secho("Uninstall failed:", fg="red", err=True)
click.echo(result.stderr, err=True)
raise click.Exit(1)
click.secho("Uninstalling mytoolkit…", fg="cyan")
_uninstall()
click.secho("mytoolkit removed.", fg="green")
# Backward compatibility: keep self_cmd group for any existing scripts
@@ -64,6 +114,5 @@ def self_cmd():
pass
# Register commands in the group for backward compatibility
self_cmd.add_command(update_cmd, name="update")
self_cmd.add_command(uninstall_cmd, name="uninstall")
+403
View File
@@ -0,0 +1,403 @@
"""Server management for workspace projects."""
import os
import signal
import subprocess
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Literal, Optional
import click
Mode = Literal["dev", "serve"]
@dataclass(frozen=True)
class Service:
name: str
serve_port: int
dev_port: Optional[int] = None
description: str = ""
project_dir: Optional[Path] = None
serve_cmd: Optional[list[str]] = None
dev_cmd: Optional[list[str]] = None
@property
def manageable(self) -> bool:
return self.project_dir is not None and self.serve_cmd is not None
@property
def has_dev(self) -> bool:
return self.dev_port is not None and self.dev_cmd is not None
def port_for(self, dev: bool) -> Optional[int]:
return self.dev_port if dev else self.serve_port
def cmd_for(self, dev: bool) -> Optional[list[str]]:
return self.dev_cmd if dev else self.serve_cmd
KNOWN_SERVICES: list[Service] = [
Service("metabot-api", 9100, description="MetaBot HTTP API"),
Service("metabot-memory", 8100, description="MetaBot MetaMemory"),
Service(
"mywebpage",
serve_port=3010,
dev_port=3000,
description="APAAM Lab website (Docusaurus)",
project_dir=Path.home() / "Documents" / "myWork" / "sysu" / "04-学术积累" / "mywebpage",
serve_cmd=["npm", "run", "serve"],
dev_cmd=["npm", "run", "dev"],
),
Service(
"apaam-webpage",
serve_port=3011,
dev_port=3001,
description="Phynexis docs (Docusaurus)",
project_dir=Path.home() / "Documents" / "myResearch" / "myProjects" / "apaam" / "repo" / "webpage",
serve_cmd=["npm", "run", "serve"],
dev_cmd=["npm", "run", "dev"],
),
Service(
"xiaohe-webpage",
serve_port=3012,
dev_port=3002,
description="Xiaohe company website (Docusaurus)",
project_dir=Path.home() / "Documents" / "myResearch" / "myProjects" / "apaam" / "repo" / "xiaohe-webpage",
serve_cmd=["npm", "run", "serve"],
dev_cmd=["npm", "run", "dev"],
),
Service("myslides", 3456, description="Slidev dev server"),
]
_BY_NAME: dict[str, Service] = {s.name: s for s in KNOWN_SERVICES}
_ALL_NAMES = list(_BY_NAME.keys())
_MANAGEABLE_NAMES = [s.name for s in KNOWN_SERVICES if s.manageable]
def manageable_services() -> list[Service]:
return [s for s in KNOWN_SERVICES if s.manageable]
def get_service(name: str) -> Service:
return _BY_NAME[name]
def no_color_env() -> dict:
env = os.environ.copy()
env["NO_COLOR"] = "1"
return env
def _listening_map() -> dict[int, dict]:
"""Single batched lsof query for all TCP listening sockets, keyed by port."""
try:
result = subprocess.run(
["lsof", "-nP", "-iTCP", "-sTCP:LISTEN"],
capture_output=True,
text=True,
errors="replace",
timeout=5,
)
except (subprocess.TimeoutExpired, FileNotFoundError):
return {}
if result.returncode != 0 or not result.stdout.strip():
return {}
by_port: dict[int, dict] = {}
for line in result.stdout.strip().split("\n")[1:]:
parts = line.split()
if len(parts) < 9:
continue
listen = next((t for t in reversed(parts) if ":" in t and not t.startswith("(")), None)
if not listen:
continue
try:
port = int(listen.rsplit(":", 1)[1])
except (ValueError, IndexError):
continue
by_port[port] = {
"command": parts[0],
"pid": parts[1],
"user": parts[2],
"listen": listen,
}
return by_port
def _port_status(port: int) -> Optional[dict]:
return _listening_map().get(port)
def _pid_command(pid: str) -> str:
try:
result = subprocess.run(
["ps", "-p", pid, "-o", "comm="],
capture_output=True,
text=True,
timeout=2,
)
if result.returncode == 0:
return result.stdout.strip()
except (subprocess.TimeoutExpired, FileNotFoundError):
pass
return ""
def _resolve(svc: Service, dev: bool) -> tuple[Optional[int], Optional[list[str]], Mode]:
return svc.port_for(dev), svc.cmd_for(dev), "dev" if dev else "serve"
@click.group(name="server")
def server_cmd():
"""Manage workspace dev/serve servers."""
pass
def _print_row(name: str, mode: str, port: int, desc: str, info: Optional[dict]) -> bool:
if info:
pid = info.get("pid", "?")
click.secho(
f"{name:<16} {mode:<6} {port:<6} {'running':<10} {pid:<8} {desc}",
fg="green",
)
return True
click.secho(
f"{name:<16} {mode:<6} {port:<6} {'stopped':<10} {'-':<8} {desc}",
fg="black",
)
return False
@server_cmd.command("list")
def server_list():
"""List all known services (serve + dev modes) and their status."""
click.echo("")
click.secho(
f"{'Service':<16} {'Mode':<6} {'Port':<6} {'Status':<10} {'PID':<8} {'Description'}",
fg="bright_blue",
bold=True,
)
click.echo("-" * 90)
listening = _listening_map()
running = 0
total = 0
for svc in KNOWN_SERVICES:
primary_label = "serve" if svc.serve_cmd is not None else "-"
if _print_row(svc.name, primary_label, svc.serve_port, svc.description, listening.get(svc.serve_port)):
running += 1
total += 1
if svc.has_dev:
assert svc.dev_port is not None
if _print_row("", "dev", svc.dev_port, "(hot reload)", listening.get(svc.dev_port)):
running += 1
total += 1
click.echo("-" * 90)
click.echo(f"Total: {total} ports, {running} running, {total - running} stopped")
click.echo("")
@server_cmd.command("check")
@click.argument("port", type=int)
def server_check(port):
"""Check if a specific port is open."""
info = _port_status(port)
if info:
cmd = _pid_command(info.get("pid", ""))
click.secho(
f"Port {port} is open (PID {info.get('pid', '?')}, {cmd or info.get('command', '')})",
fg="green",
)
else:
click.secho(f"Port {port} is not listening", fg="red")
@server_cmd.command("scan")
@click.argument("start", type=int, default=3000)
@click.argument("end", type=int, default=4000)
def server_scan(start, end):
"""Scan a port range for listening services."""
click.echo(f"Scanning ports {start}-{end} ...")
listening = _listening_map()
found = 0
for port in range(start, end + 1):
info = listening.get(port)
if info:
cmd = _pid_command(info.get("pid", ""))
click.secho(
f" {port:<5} {cmd or info.get('command', '')} (PID {info.get('pid', '?')})",
fg="green",
)
found += 1
if found == 0:
click.echo("No listening ports found.")
else:
click.echo(f"\nFound {found} open port(s).")
@server_cmd.command("start")
@click.argument("name", type=click.Choice(_MANAGEABLE_NAMES, case_sensitive=False))
@click.option("--dev", is_flag=True, help="Start hot-reload dev server on the dev port (default: serve)")
@click.option("--rebuild", is_flag=True, help="Force npm run build before serve (no effect with --dev)")
def server_start(name, dev, rebuild):
"""Start a server in the background.
By default, serve mode is used (production build) on the serve port.
Use --dev to start the hot-reload dev server on the dev port instead.
"""
svc = get_service(name)
port, cmd, mode = _resolve(svc, dev)
if port is None or cmd is None:
click.secho(f"{name} has no {mode} mode configured", fg="red")
return
assert svc.project_dir is not None # _MANAGEABLE_NAMES choice guarantees this
if _port_status(port) is not None:
click.secho(f"{name} ({mode}) is already running on port {port}", fg="yellow")
return
if not svc.project_dir.exists():
click.secho(f"Project directory not found: {svc.project_dir}", fg="red")
return
env = no_color_env()
if not dev:
build_dir = svc.project_dir / "build"
if rebuild or not build_dir.exists():
reason = "forced rebuild" if rebuild else "build directory missing"
click.secho(f"Running npm run build ({reason}) ...", fg="cyan")
build_proc = subprocess.run(
["npm", "run", "build"],
cwd=svc.project_dir,
env=env,
capture_output=True,
text=True,
)
if build_proc.returncode != 0:
click.secho(f"Build failed for {name}", fg="red")
click.echo(build_proc.stderr[-500:] if build_proc.stderr else "")
return
log_file = svc.project_dir / "logs" / f"{svc.name}-{mode}.log"
log_file.parent.mkdir(exist_ok=True)
full_cmd = ["nohup"] + cmd
click.secho(f"Starting {name} ({svc.description}) on port {port} ({mode}) ...", fg="cyan")
with open(log_file, "a") as log:
proc = subprocess.Popen(
full_cmd,
cwd=svc.project_dir,
stdout=log,
stderr=subprocess.STDOUT,
start_new_session=True,
env=env,
)
max_wait = 20 if dev else 30
for _ in range(max_wait * 2):
time.sleep(0.5)
if _port_status(port) is not None:
click.secho(f"{name} ({mode}) started successfully (PID {proc.pid})", fg="green")
return
click.secho(f"{name} may have failed to start (check {log_file})", fg="yellow")
def _kill_port(name: str, mode: Mode, port: int):
info = _port_status(port)
if info is None:
click.secho(f"{name} ({mode}) is not running on port {port}", fg="yellow")
return
pid = info["pid"]
try:
os.kill(int(pid), signal.SIGTERM)
click.secho(f"Sent SIGTERM to {name} ({mode}, PID {pid})", fg="green")
except ProcessLookupError:
click.secho(f"Process {pid} not found", fg="yellow")
return
for _ in range(10):
time.sleep(0.5)
if _port_status(port) is None:
click.secho(f"{name} ({mode}) stopped", fg="green")
return
try:
os.kill(int(pid), signal.SIGKILL)
click.secho(f"Sent SIGKILL to {name} ({mode}, PID {pid})", fg="red")
except ProcessLookupError:
pass
@server_cmd.command("stop")
@click.argument("name", type=click.Choice(_MANAGEABLE_NAMES, case_sensitive=False))
@click.option("--dev", is_flag=True, help="Stop the dev-port instance instead of serve")
@click.option("--all", "all_modes", is_flag=True, help="Stop both dev and serve instances")
def server_stop(name, dev, all_modes):
"""Stop a running server."""
svc = get_service(name)
if all_modes:
targets: list[tuple[Mode, int]] = [("serve", svc.serve_port)]
if svc.has_dev:
assert svc.dev_port is not None
targets.append(("dev", svc.dev_port))
else:
port, _, mode = _resolve(svc, dev)
if port is None:
click.secho(f"{name} has no {mode} mode configured", fg="red")
return
targets = [(mode, port)]
for target_mode, target_port in targets:
_kill_port(name, target_mode, target_port)
@server_cmd.command("restart")
@click.argument("name", type=click.Choice(_MANAGEABLE_NAMES, case_sensitive=False))
@click.option("--dev", is_flag=True, help="Restart the dev instance (default: serve)")
@click.option("--rebuild", is_flag=True, help="Force npm run build before serve (no effect with --dev)")
def server_restart(name, dev, rebuild):
"""Restart a server."""
ctx = click.get_current_context()
ctx.invoke(server_stop, name=name, dev=dev, all_modes=False)
ctx.invoke(server_start, name=name, dev=dev, rebuild=rebuild)
@server_cmd.command("status")
@click.argument("name", type=click.Choice(_ALL_NAMES, case_sensitive=False))
def server_status(name):
"""Show detailed status of a service (both serve and dev ports)."""
svc = get_service(name)
click.echo("")
click.secho(f"Service: {svc.name}", bold=True)
click.echo(f"Desc: {svc.description}")
if svc.project_dir:
click.echo(f"Project: {svc.project_dir}")
def _show(label: str, port: Optional[int]):
if port is None:
return
info = _port_status(port)
if info:
cmd = _pid_command(info.get("pid", ""))
click.secho(
f"{label:<8} {port} running (PID {info.get('pid', '?')}, {cmd or info.get('command', '')})",
fg="green",
)
else:
click.secho(f"{label:<8} {port} stopped", fg="black")
_show("Serve:", svc.serve_port)
_show("Dev:", svc.dev_port)
click.echo("")
+77
View File
@@ -0,0 +1,77 @@
"""Templates registry CLI."""
from pathlib import Path
import click
from bin import templates_registry
@click.group(name="templates")
def templates_cmd():
"""Manage external templates registry."""
pass
@templates_cmd.command("register")
@click.argument("path", type=click.Path(exists=True, file_okay=False, dir_okay=True))
def templates_register(path):
"""Register a directory as the templates root."""
resolved = templates_registry.set_root(Path(path))
click.secho(f"Registered: {resolved}", fg="green")
@templates_cmd.command("show")
def templates_show():
"""Show the current registry and available templates."""
if not templates_registry.CONFIG_PATH.exists():
click.echo("No registry. Run: mytoolkit templates register <path>")
return
import json
with open(templates_registry.CONFIG_PATH, encoding="utf-8") as f:
data = json.load(f)
raw = data.get("dir")
click.echo(f"Registry: {templates_registry.CONFIG_PATH}")
click.echo(f"Root: {raw}")
if not raw or not Path(raw).is_dir():
click.secho("(root path missing)", fg="red")
return
root = Path(raw)
pdf_dir = root / "md-to-pdf"
docx_dir = root / "md-to-docx"
click.echo("\nmd-to-pdf:")
if pdf_dir.is_dir():
entries = sorted(
(p.name, p / "template.typ")
for p in pdf_dir.iterdir()
if (p / "template.typ").exists()
)
if not entries:
click.echo(" (none)")
else:
width = max(len(n) for n, _ in entries)
for name, path in entries:
click.echo(f" - {name:<{width}} {path}")
else:
click.secho(" (subdirectory missing)", fg="yellow")
click.echo("\nmd-to-docx:")
if docx_dir.is_dir():
entries = sorted(
(p.name, p / "template.docx")
for p in docx_dir.iterdir()
if (p / "template.docx").exists()
)
if not entries:
click.echo(" (none)")
else:
width = max(len(n) for n, _ in entries)
for name, path in entries:
click.echo(f" - {name:<{width}} {path}")
else:
click.secho(" (subdirectory missing)", fg="yellow")
+89
View File
@@ -0,0 +1,89 @@
"""Webpage project utilities (Docusaurus build / clear / etc.).
Project metadata lives in `server.py` so that adding a new webpage there
automatically wires it up here.
"""
import subprocess
import time
import click
from bin.commands.server import manageable_services, get_service, no_color_env
_PROJECTS = manageable_services()
_NAMES = [s.name for s in _PROJECTS]
def _run_npm(name: str, script: str, label: str) -> None:
"""Run `npm run <script>` in the project's directory, streaming output."""
svc = get_service(name)
assert svc.project_dir is not None # _NAMES is manageable services only
if not svc.project_dir.exists():
click.secho(f"Project directory not found: {svc.project_dir}", fg="red")
raise click.Abort()
click.secho(f"{label} {svc.name} ({svc.description}) ...", fg="cyan")
started = time.time()
proc = subprocess.run(["npm", "run", script], cwd=svc.project_dir, env=no_color_env())
elapsed = time.time() - started
if proc.returncode != 0:
click.secho(f"{script} failed for {svc.name} (exit {proc.returncode})", fg="red")
raise click.Abort()
click.secho(f"{svc.name} {script} done in {elapsed:.1f}s", fg="green")
@click.group(name="webpage")
def webpage_cmd():
"""Manage Docusaurus webpage projects."""
pass
@webpage_cmd.command("list")
def webpage_list():
"""List managed webpage projects and their build status."""
click.echo("")
click.secho(
f"{'Name':<16} {'Build':<12} {'Description':<36} {'Project'}",
fg="bright_blue",
bold=True,
)
click.echo("-" * 100)
for svc in _PROJECTS:
assert svc.project_dir is not None # _PROJECTS is manageable services only
build_dir = svc.project_dir / "build"
if build_dir.exists():
build_status = time.strftime("%m-%d %H:%M", time.localtime(build_dir.stat().st_mtime))
color = "green"
else:
build_status = "missing"
color = "yellow"
click.secho(
f"{svc.name:<16} {build_status:<12} {svc.description:<36} {svc.project_dir}",
fg=color,
)
click.echo("")
@webpage_cmd.command("build")
@click.argument("name", type=click.Choice(_NAMES, case_sensitive=False))
def webpage_build(name):
"""Build a webpage project (`npm run build`)."""
_run_npm(name, "build", "Building")
@webpage_cmd.command("clear")
@click.argument("name", type=click.Choice(_NAMES, case_sensitive=False))
def webpage_clear(name):
"""Clear Docusaurus build artifacts and cache (`npm run clear`)."""
_run_npm(name, "clear", "Clearing")
@webpage_cmd.command("rebuild")
@click.argument("name", type=click.Choice(_NAMES, case_sensitive=False))
def webpage_rebuild(name):
"""Clear cache then build (`npm run clear && npm run build`)."""
_run_npm(name, "clear", "Clearing")
_run_npm(name, "build", "Building")
-48
View File
@@ -1,48 +0,0 @@
{
"paths": {
"study": "~/Documents/myStudy/self_learning",
"research": "~/Documents/myResearch",
"apaam": "~/Documents/myResearch/myProjects/apaam",
"tianhe": "~/Documents/myWork/sysu/0_中山大学/hpc_tianhe",
"academia": "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/myacademia",
"webpage": "~/Documents/myWork/sysu/4_成果归档/mywebpage",
"slides": "~/Documents/myWork/sysu/4_成果归档/myslides",
"myclaude": "~/Documents/myResearch/myProjects/apaam/repo/myclaude",
"myagent": "~/Documents/myResearch/myProjects/apaam/repo/myagent"
},
"ssh_hosts": {
"tianhe": {
"host": "sysu_lchuangxy_1@172.16.31.31",
"port": 6666,
"key": "~/Documents/myWork/sysu/0_中山大学/hpc_tianhe/sysu_lchuangxy_1.id"
},
"starlight": {
"host": "sysu_lchuang_1@proxy.nscc-gz.cn",
"port": 23
}
},
"git_proxies": {
"fastgithub": "http://127.0.0.1:38457",
"pandafan": "http://127.0.0.1:10080"
},
"openfoam": {
"dmg_path": "~/Documents/myResearch/myProjects/apaam/repo/OpenFOAM.dmg",
"mount_point": "/Volumes/OpenFOAM",
"build_path": "/Volumes/OpenFOAM/openfoam/build"
},
"cv": "~/Documents/Personal/resume_cv/cv_lai_maintaining_latex/cv_lai_maintaining",
"api_keys": {
"deepseek": "YOUR_DEEPSEEK_API_KEY_HERE",
"qwen": "YOUR_QWEN_API_KEY_HERE"
},
"feishu_apps": {
"claude": {
"app_id": "YOUR_CLAUDE_APP_ID",
"app_secret": "YOUR_CLAUDE_APP_SECRET"
},
"myagent": {
"app_id": "YOUR_MYAGENT_APP_ID",
"app_secret": "YOUR_MYAGENT_APP_SECRET"
}
}
}
+14 -3
View File
@@ -3,23 +3,34 @@
import json
from pathlib import Path
CONFIG_PATH = Path.home() / ".config" / "mytoolkit" / "env.json"
_LEGACY_PATH = Path(__file__).parent / "config.json"
class Config:
"""Simple config manager for environment variables."""
def __init__(self):
self._config_path = Path(__file__).parent / "config.json"
self._config_path = CONFIG_PATH
self._migrate_legacy()
self._data = self._load()
def _migrate_legacy(self) -> None:
"""One-shot move of legacy bin/config.json into the user config dir."""
if _LEGACY_PATH.exists() and not self._config_path.exists():
self._config_path.parent.mkdir(parents=True, exist_ok=True)
_LEGACY_PATH.rename(self._config_path)
def _load(self) -> dict:
if self._config_path.exists():
with open(self._config_path) as f:
with open(self._config_path, encoding="utf-8") as f:
return json.load(f)
return {"vars": {}}
def save(self):
"""Save config to file."""
with open(self._config_path, "w") as f:
self._config_path.parent.mkdir(parents=True, exist_ok=True)
with open(self._config_path, "w", encoding="utf-8") as f:
json.dump(self._data, f, indent=2, ensure_ascii=False)
f.write("\n")
+164 -4
View File
@@ -9,6 +9,151 @@ import click
from bin import templates_registry
from bin.utils import run_command
# Typst callouts exported by templates/md-to-pdf/{textbook,manual}/template.typ.
# Keep this tuple and _extra_imports (below) in sync when adding or removing `#let` callouts.
# Pandoc escapes leading `#`, so these names are wrapped in a long-enough `{=typst}` fence
# (length chosen so inner ``` from Pandoc does not terminate the fence early).
_TY_CALL_NAMES: tuple[str, ...] = (
"info-box",
"tip-box",
"warn-box",
"danger-box",
"theorem",
"lemma",
"proof",
"example",
"exercise",
)
_TY_CALL_ALT = "|".join(
re.escape(n) for n in sorted(_TY_CALL_NAMES, key=len, reverse=True)
)
_TY_CALL_START = re.compile(
rf"^(\s{{0,3}})(#(?:{_TY_CALL_ALT}))(?![A-Za-z0-9-])",
re.MULTILINE,
)
def _odd_fenced_code_before(md: str, pos: int) -> bool:
"""True if ``pos`` lies inside an odd number of ``` fenced regions (heuristic)."""
in_fence = False
for line in md[:pos].split("\n"):
stripped = line.lstrip()
if stripped.startswith("```"):
in_fence = not in_fence
return in_fence
def _skip_quoted(md: str, i: int) -> int:
quote = md[i]
i += 1
n = len(md)
while i < n:
ch = md[i]
if ch == "\\" and i + 1 < n:
i += 2
continue
if ch == quote:
return i + 1
i += 1
return i
def _consume_balanced(md: str, i: int, open_c: str, close_c: str) -> int | None:
"""If ``md[i]`` is ``open_c``, return index one past matching ``close_c``."""
if i >= len(md) or md[i] != open_c:
return None
depth = 0
pos = i
n = len(md)
while pos < n:
ch = md[pos]
if ch in ('"', "'"):
pos = _skip_quoted(md, pos)
continue
if ch == open_c:
depth += 1
elif ch == close_c:
depth -= 1
if depth == 0:
return pos + 1
pos += 1
return None
def _md_fragment_to_typst(fragment: str) -> str:
fragment = fragment.strip("\n")
if not fragment.strip():
return ""
result = run_command(
["pandoc", "-f", "markdown-citations", "-t", "typst", "-"],
input=fragment,
capture_output=True,
text=True,
)
return result.stdout.rstrip()
def _longest_backtick_run(s: str) -> int:
longest = 0
i = 0
n = len(s)
while i < n:
if s[i] == "`":
j = i
while j < n and s[j] == "`":
j += 1
longest = max(longest, j - i)
i = j
else:
i += 1
return longest
def _typst_raw_fence(inner: str) -> str:
"""Fence long enough to contain Pandoc typst that may include ``` code blocks."""
run = _longest_backtick_run(inner)
return "`" * max(3, run + 1)
def preprocess_typst_callouts(md: str) -> str:
"""Wrap template Typst callouts in Pandoc raw Typst blocks so they compile.
Bodies are converted with Pandoc separately so Markdown and math still work.
Markdown links that use ``[...](...)`` inside a callout can break bracket matching.
"""
out: list[str] = []
last = 0
for m in _TY_CALL_START.finditer(md):
if _odd_fenced_code_before(md, m.start()):
continue
idx = m.start(2)
pos = m.end(2)
while pos < len(md) and md[pos] in " \t":
pos += 1
if pos < len(md) and md[pos] == "(":
end_paren = _consume_balanced(md, pos, "(", ")")
if end_paren is None:
continue
pos = end_paren
while pos < len(md) and md[pos] in " \t":
pos += 1
if pos >= len(md) or md[pos] != "[":
continue
bracket_open = pos
after_body = _consume_balanced(md, bracket_open, "[", "]")
if after_body is None:
continue
body_inner = md[bracket_open + 1 : after_body - 1]
prefix = md[idx : bracket_open + 1]
inner_typ = _md_fragment_to_typst(body_inner)
fence = _typst_raw_fence(prefix + "\n" + inner_typ)
replacement = f"{fence}{{=typst}}\n{prefix}\n{inner_typ}\n]\n{fence}\n"
out.append(md[last : m.start()])
out.append(replacement)
last = after_body
out.append(md[last:])
return "".join(out)
def md_to_typ(inputs: list[Path]) -> str:
"""Convert Markdown files to Typst via pandoc."""
@@ -16,6 +161,7 @@ def md_to_typ(inputs: list[Path]) -> str:
for inp in inputs:
with open(inp, "r", encoding="utf-8") as f:
md_content += f.read() + "\n\n"
md_content = preprocess_typst_callouts(md_content)
result = run_command(
["pandoc", "-f", "markdown-citations", "-t", "typst", "-"],
input=md_content,
@@ -23,8 +169,11 @@ def md_to_typ(inputs: list[Path]) -> str:
text=True,
)
typ = result.stdout
# Pandoc outputs #horizontalrule which Typst does not define
typ = typ.replace("#horizontalrule", "#line(length: 100%)")
# Pandoc outputs #horizontalrule which Typst does not define; match template rule-blue
typ = typ.replace(
"#horizontalrule",
'#line(length: 100%, stroke: 0.65pt + rgb("#3467A8").lighten(46%))',
)
# Remove Pandoc auto-generated heading labels to avoid named-destination
# links that break with non-ASCII characters in some PDF readers.
typ = re.sub(r"(^=+ .*?)\n<[^>\n]+>\n", r"\1\n", typ, flags=re.MULTILINE)
@@ -43,7 +192,7 @@ def expand_bookmarks(pdf_path: Path, max_level: int) -> None:
writer = PdfWriter()
writer.clone_document_from_reader(reader)
outline_root = reader.trailer["/Root"].get("/Outlines")
outline_root = writer._root_object.get("/Outlines")
if not outline_root:
writer.write(str(pdf_path))
return
@@ -95,8 +244,19 @@ def build_pdf(
tmp_path = templates_dir / tmp_name
rel_template = f"{template}/template.typ"
# Pandoc raw Typst callouts (#theorem, #info-box, …) live in the driver file; they must
# resolve to symbols exported from the template module (not only inside #show: …).
_extra_imports = {
"textbook": "theorem, lemma, proof, example, exercise",
"manual": "info-box, tip-box, warn-box, danger-box",
}
extra = _extra_imports.get(template, "")
import_list = f"{template}, three-line-table"
if extra:
import_list += f", {extra}"
with open(tmp_path, "w", encoding="utf-8") as f:
f.write(f'#import "{rel_template}": {template}, three-line-table\n\n')
f.write(f'#import "{rel_template}": {import_list}\n\n')
f.write(f"#show: {template}.with()\n\n")
f.write(typ_body)
+60
View File
@@ -0,0 +1,60 @@
"""External templates registry for md→pdf / md→docx conversion."""
import json
from pathlib import Path
import click
CONFIG_PATH = Path.home() / ".config" / "mytoolkit" / "templates.json"
def _read() -> dict:
if not CONFIG_PATH.exists():
return {}
with open(CONFIG_PATH, encoding="utf-8") as f:
return json.load(f)
def _write(data: dict) -> None:
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
def set_root(path: Path) -> Path:
"""Register the templates root directory. Returns the resolved path."""
resolved = Path(path).expanduser().resolve()
_write({"dir": str(resolved)})
return resolved
def get_root() -> Path:
"""Return the registered templates root, or raise UsageError if missing."""
data = _read()
raw = data.get("dir")
if not raw:
raise click.UsageError(
"Templates not registered. Run: mytoolkit templates register <path>"
)
root = Path(raw)
if not root.is_dir():
raise click.UsageError(
f"Registered templates dir does not exist: {root}\n"
"Re-run: mytoolkit templates register <path>"
)
return root
def get_md_to_pdf_root() -> Path:
sub = get_root() / "md-to-pdf"
if not sub.is_dir():
raise click.UsageError(f"Subdirectory missing: {sub}")
return sub
def get_md_to_docx_root() -> Path:
sub = get_root() / "md-to-docx"
if not sub.is_dir():
raise click.UsageError(f"Subdirectory missing: {sub}")
return sub
+4 -3
View File
@@ -2,6 +2,7 @@
import functools
import subprocess
import sys
import click
@@ -15,15 +16,15 @@ def handle_errors(func):
return func(*args, **kwargs)
except FileNotFoundError as e:
click.secho(f"Command not found: {e.filename}", fg="red", err=True)
raise click.Exit(1)
sys.exit(1)
except subprocess.CalledProcessError as e:
click.secho(f"Command failed with exit code {e.returncode}", fg="red", err=True)
if e.stderr:
click.echo(e.stderr, err=True)
raise click.Exit(1)
sys.exit(1)
except KeyboardInterrupt:
click.echo("\nAborted.")
raise click.Exit(130)
sys.exit(130)
return wrapper
+1
View File
@@ -5,6 +5,7 @@ description = "Personal CLI toolkit"
requires-python = ">=3.9"
dependencies = [
"click>=8.0",
"openai>=1.60",
"pillow>=11.3.0",
"pypdf>=6.10.1",
]
Generated
+436
View File
@@ -6,6 +6,58 @@ resolution-markers = [
"python_full_version < '3.10'",
]
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
]
[[package]]
name = "anyio"
version = "4.12.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.10'",
]
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.10'" },
{ name = "idna", marker = "python_full_version < '3.10'" },
{ name = "typing-extensions", marker = "python_full_version < '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
]
[[package]]
name = "anyio"
version = "4.13.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.10'",
]
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version == '3.10.*'" },
{ name = "idna", marker = "python_full_version >= '3.10'" },
{ name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
]
[[package]]
name = "certifi"
version = "2026.4.22"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
]
[[package]]
name = "click"
version = "8.1.8"
@@ -45,6 +97,190 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "distro"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
[[package]]
name = "h11"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "anyio", version = "4.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]]
name = "idna"
version = "3.13"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" },
]
[[package]]
name = "jiter"
version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6e/c1/0cddc6eb17d4c53a99840953f95dd3accdc5cfc7a337b0e9b26476276be9/jiter-0.14.0.tar.gz", hash = "sha256:e8a39e66dac7153cf3f964a12aad515afa8d74938ec5cc0018adcdae5367c79e", size = 165725, upload-time = "2026-04-10T14:28:42.01Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/2e/a9959997739c403378d0a4a3a1c4ed80b60aeace216c4d37b303a9fc60a4/jiter-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:02f36a5c700f105ac04a6556fe664a59037a2c200db3b7e88784fac2ddf02531", size = 316927, upload-time = "2026-04-10T14:25:40.753Z" },
{ url = "https://files.pythonhosted.org/packages/27/72/b6de8a531e0adbadd839bec301165feb1fccf00e9ff55073ba2dd20f0043/jiter-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41eab6c09ceffb6f0fe25e214b3068146edb1eda3649ca2aee2a061029c7ba2e", size = 321181, upload-time = "2026-04-10T14:25:42.621Z" },
{ url = "https://files.pythonhosted.org/packages/db/d8/2040b9efa13c917f855c40890ae4119fe02c25b7c7677d5b4fa820a851fc/jiter-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cf4d4c109641f9cfaf4a7b6aebd51654e405cd00fa9ebbf87163b8b97b325aa", size = 347387, upload-time = "2026-04-10T14:25:44.212Z" },
{ url = "https://files.pythonhosted.org/packages/49/62/655c0ad5ce6a8e90f9068c175b8a236877d753e460762b3183c136db1c5b/jiter-0.14.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80c7b41a628e6be2213ad0ece763c5f88aa5ee003fa394d58acaaee1f4b8342", size = 373083, upload-time = "2026-04-10T14:25:45.55Z" },
{ url = "https://files.pythonhosted.org/packages/f1/66/549c40fa068f08710b7570869c306a051eb67a29758bd64f4114f730554c/jiter-0.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb3dbf7cc0d4dbe73cce307ebe7eefa7f73a7d3d854dd119ea0c243f03e40927", size = 463639, upload-time = "2026-04-10T14:25:47.452Z" },
{ url = "https://files.pythonhosted.org/packages/25/2f/97a32a05fed14ed58a18e181fdfb619e05163f3726b54ee6080ec0539c09/jiter-0.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7054adcdeb06b46efd17b5734f75817a44a2d06d3748e36c3a023a1bb52af9ec", size = 380735, upload-time = "2026-04-10T14:25:49.305Z" },
{ url = "https://files.pythonhosted.org/packages/2a/3b/4347e1d6c2a973d653bbb7a2d671a2d2426e54b52ba735b8ff0d0a29b75c/jiter-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d597cd1bf6790376f3fffc7c708766e57301d99a19314824ea0ccc9c3c70e1e2", size = 358632, upload-time = "2026-04-10T14:25:50.931Z" },
{ url = "https://files.pythonhosted.org/packages/ef/24/ca452fbf2ea33548ed30ce68a39a50442d3f7c9bf0704a7af958a930c057/jiter-0.14.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:df63a14878da754427926281626fd3ee249424a186e25a274e78176d42945264", size = 359969, upload-time = "2026-04-10T14:25:52.381Z" },
{ url = "https://files.pythonhosted.org/packages/e3/a3/94470a0d199287caabeb4da2bb2ae5f6d17f3cf05dfc975d7cb064d58e0f/jiter-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ea73187627bcc5810e085df715e8a99da8bdfd96a7eb36b4b4df700ba6d4c9c", size = 397529, upload-time = "2026-04-10T14:25:53.801Z" },
{ url = "https://files.pythonhosted.org/packages/cf/71/6768edc09d7c45c39f093feb3de105fa718a3e982b5208b8a2ed6382b44b/jiter-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9f541eaf7bb8382367a1a23d6fc3d6aad57f8dd8c18c3c17f838bee20f217220", size = 522342, upload-time = "2026-04-10T14:25:55.396Z" },
{ url = "https://files.pythonhosted.org/packages/3d/6b/5c2e17559a0f4e96e934479f7137df46c939e983fa05244e674815befb73/jiter-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:107465250de4fce00fdb47166bcd51df8e634e049541174fe3c71848e44f52ce", size = 556784, upload-time = "2026-04-10T14:25:56.927Z" },
{ url = "https://files.pythonhosted.org/packages/b1/83/c25f3556a60fc74d11199100f1b6cc0c006b815c8494dea8ca16fe398732/jiter-0.14.0-cp310-cp310-win32.whl", hash = "sha256:ffb2a08a406465bb076b7cc1df41d833106d3cf7905076cc73f0cb90078c7d10", size = 208439, upload-time = "2026-04-10T14:25:58.796Z" },
{ url = "https://files.pythonhosted.org/packages/2e/99/781a1b413f0989b7f2ea203b094b331685f1a35e52e0a45e5d000ecaab27/jiter-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb8b682d10cb0cce7ff4c1af7244af7022c9b01ae16d46c357bdd0df13afb25d", size = 204558, upload-time = "2026-04-10T14:26:00.208Z" },
{ url = "https://files.pythonhosted.org/packages/8a/1f/198ae537fccb7080a0ed655eb56abf64a92f79489dfbf79f40fa34225bcd/jiter-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7e791e247b8044512e070bd1f3633dc08350d32776d2d6e7473309d0edf256a2", size = 316896, upload-time = "2026-04-10T14:26:01.986Z" },
{ url = "https://files.pythonhosted.org/packages/cf/34/da67cff3fce964a36d03c3e365fb0f8726ade2a6cfd4d3c70107e216ead6/jiter-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71527ce13fd5a0c4e40ad37331f8c547177dbb2dd0a93e5278b6a5eecf748804", size = 321085, upload-time = "2026-04-10T14:26:03.364Z" },
{ url = "https://files.pythonhosted.org/packages/ed/36/4c72e67180d4e71a4f5dcf7886d0840e83c49ab11788172177a77570326e/jiter-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c4a7ab56f746014874f2c525584c0daca1dec37f66fd707ecef3b7e5c2228c", size = 347393, upload-time = "2026-04-10T14:26:05.314Z" },
{ url = "https://files.pythonhosted.org/packages/bc/db/9b39e09ceafa9878235c0fc29e3e3f9b12a4c6a98ea3085b998cadf3accc/jiter-0.14.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:376e9dafff914253bb9d46cdc5f7965607fbe7feb0a491c34e35f92b2770702e", size = 372937, upload-time = "2026-04-10T14:26:06.884Z" },
{ url = "https://files.pythonhosted.org/packages/b0/96/0dcba1d7a82c1b720774b48ef239376addbaf30df24c34742ac4a57b67b2/jiter-0.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23ad2a7a9da1935575c820428dd8d2490ce4d23189691ce33da1fc0a58e14e1c", size = 463646, upload-time = "2026-04-10T14:26:08.345Z" },
{ url = "https://files.pythonhosted.org/packages/f1/e3/f61b71543e746e6b8b805e7755814fc242715c16f1dba58e1cbccb8032c2/jiter-0.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54b3ddf5786bc7732d293bba3411ac637ecfa200a39983166d1df86a59a43c9f", size = 380225, upload-time = "2026-04-10T14:26:10.161Z" },
{ url = "https://files.pythonhosted.org/packages/ad/5e/0ddeb7096aca099114abe36c4921016e8d251e6f35f5890240b31f1f60ae/jiter-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c001d5a646c2a50dc055dd526dad5d5245969e8234d2b1131d0451e81f3a373", size = 358682, upload-time = "2026-04-10T14:26:11.574Z" },
{ url = "https://files.pythonhosted.org/packages/e9/d1/fe0c46cd7fda9cad8f1ff9ad217dc61f1e4280b21052ec6dfe88c1446ef2/jiter-0.14.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:834bb5bdabca2e91592a03d373838a8d0a1b8bbde7077ae6913fd2fc51812d00", size = 359973, upload-time = "2026-04-10T14:26:13.316Z" },
{ url = "https://files.pythonhosted.org/packages/ac/21/f5317f91729b501019184771c80d60abd89907009e7bfa6c7e348c5bdd44/jiter-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4e9178be60e229b1b2b0710f61b9e24d1f4f8556985a83ff4c4f95920eea7314", size = 397568, upload-time = "2026-04-10T14:26:15.212Z" },
{ url = "https://files.pythonhosted.org/packages/e9/05/79d8f33fb2bf168db0df5c9cd16fe440a8ada57e929d3677b22712c2568f/jiter-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7e4ccff04ec03614e62c613e976a3a5860dc9714ce8266f44328bdc8b1cab2c", size = 522535, upload-time = "2026-04-10T14:26:16.956Z" },
{ url = "https://files.pythonhosted.org/packages/5c/00/d1e3ff3d2a465e67f08507d74bafb2dcd29eba91dc939820e39e8dea38b8/jiter-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:69539d936fb5d55caf6ecd33e2e884de083ff0ea28579780d56c4403094bb8d9", size = 556709, upload-time = "2026-04-10T14:26:18.5Z" },
{ url = "https://files.pythonhosted.org/packages/60/5b/bbb2189f62ace8d95e869aa4c84c9946616f301e2d02895a6f20dcc3bba3/jiter-0.14.0-cp311-cp311-win32.whl", hash = "sha256:4927d09b3e572787cc5e0a5318601448e1ab9391bcef95677f5840c2d00eaa6d", size = 208660, upload-time = "2026-04-10T14:26:20.511Z" },
{ url = "https://files.pythonhosted.org/packages/b8/86/c500b53dcbf08575f5963e536ebd757a1f7c568272ba5d180b212c9a87fb/jiter-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:42d6ed359ac49eb922fdd565f209c57340aa06d589c84c8413e42a0f9ae1b842", size = 204659, upload-time = "2026-04-10T14:26:22.152Z" },
{ url = "https://files.pythonhosted.org/packages/75/4a/a676249049d42cb29bef82233e4fe0524d414cbe3606c7a4b311193c2f77/jiter-0.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:6dd689f5f4a5a33747b28686e051095beb214fe28cfda5e9fe58a295a788f593", size = 194772, upload-time = "2026-04-10T14:26:23.458Z" },
{ url = "https://files.pythonhosted.org/packages/5a/68/7390a418f10897da93b158f2d5a8bd0bcd73a0f9ec3bb36917085bb759ef/jiter-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb2ce3a7bc331256dfb14cefc34832366bb28a9aca81deaf43bbf2a5659e607", size = 316295, upload-time = "2026-04-10T14:26:24.887Z" },
{ url = "https://files.pythonhosted.org/packages/60/a0/5854ac00ff63551c52c6c89534ec6aba4b93474e7924d64e860b1c94165b/jiter-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5252a7ca23785cef5d02d4ece6077a1b556a410c591b379f82091c3001e14844", size = 315898, upload-time = "2026-04-10T14:26:26.601Z" },
{ url = "https://files.pythonhosted.org/packages/41/a1/4f44832650a16b18e8391f1bf1d6ca4909bc738351826bcc198bba4357f4/jiter-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c409578cbd77c338975670ada777add4efd53379667edf0aceea730cabede6fb", size = 343730, upload-time = "2026-04-10T14:26:28.326Z" },
{ url = "https://files.pythonhosted.org/packages/48/64/a329e9d469f86307203594b1707e11ae51c3348d03bfd514a5f997870012/jiter-0.14.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ede4331a1899d604463369c730dbb961ffdc5312bc7f16c41c2896415b1304a", size = 370102, upload-time = "2026-04-10T14:26:30.089Z" },
{ url = "https://files.pythonhosted.org/packages/94/c1/5e3dfc59635aa4d4c7bd20a820ac1d09b8ed851568356802cf1c08edb3cf/jiter-0.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92cd8b6025981a041f5310430310b55b25ca593972c16407af8837d3d7d2ca01", size = 461335, upload-time = "2026-04-10T14:26:31.911Z" },
{ url = "https://files.pythonhosted.org/packages/e3/1b/dd157009dbc058f7b00108f545ccb72a2d56461395c4fc7b9cfdccb00af4/jiter-0.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:351bf6eda4e3a7ceb876377840c702e9a3e4ecc4624dbfb2d6463c67ae52637d", size = 378536, upload-time = "2026-04-10T14:26:33.595Z" },
{ url = "https://files.pythonhosted.org/packages/91/78/256013667b7c10b8834f8e6e54cd3e562d4c6e34227a1596addccc05e38c/jiter-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1dcfbeb93d9ecd9ca128bbf8910120367777973fa193fb9a39c31237d8df165", size = 353859, upload-time = "2026-04-10T14:26:35.098Z" },
{ url = "https://files.pythonhosted.org/packages/de/d9/137d65ade9093a409fe80955ce60b12bb753722c986467aeda47faf450ad/jiter-0.14.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ae039aaef8de3f8157ecc1fdd4d85043ac4f57538c245a0afaecb8321ec951c3", size = 357626, upload-time = "2026-04-10T14:26:36.685Z" },
{ url = "https://files.pythonhosted.org/packages/2e/48/76750835b87029342727c1a268bea8878ab988caf81ee4e7b880900eeb5a/jiter-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7d9d51eb96c82a9652933bd769fe6de66877d6eb2b2440e281f2938c51b5643e", size = 393172, upload-time = "2026-04-10T14:26:38.097Z" },
{ url = "https://files.pythonhosted.org/packages/a6/60/456c4e81d5c8045279aefe60e9e483be08793828800a4e64add8fdde7f2a/jiter-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d824ca4148b705970bf4e120924a212fdfca9859a73e42bd7889a63a4ea6bb98", size = 520300, upload-time = "2026-04-10T14:26:39.532Z" },
{ url = "https://files.pythonhosted.org/packages/a8/9f/2020e0984c235f678dced38fe4eec3058cf528e6af36ebf969b410305941/jiter-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff3a6465b3a0f54b1a430f45c3c0ba7d61ceb45cbc3e33f9e1a7f638d690baf3", size = 553059, upload-time = "2026-04-10T14:26:40.991Z" },
{ url = "https://files.pythonhosted.org/packages/ef/32/e2d298e1a22a4bbe6062136d1c7192db7dba003a6975e51d9a9eecabc4c2/jiter-0.14.0-cp312-cp312-win32.whl", hash = "sha256:5dec7c0a3e98d2a3f8a2e67382d0d7c3ac60c69103a4b271da889b4e8bb1e129", size = 206030, upload-time = "2026-04-10T14:26:42.517Z" },
{ url = "https://files.pythonhosted.org/packages/36/ac/96369141b3d8a4a8e4590e983085efe1c436f35c0cda940dd76d942e3e40/jiter-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:fc7e37b4b8bc7e80a63ad6cfa5fc11fab27dbfea4cc4ae644b1ab3f273dc348f", size = 201603, upload-time = "2026-04-10T14:26:44.328Z" },
{ url = "https://files.pythonhosted.org/packages/01/c3/75d847f264647017d7e3052bbcc8b1e24b95fa139c320c5f5066fa7a0bdd/jiter-0.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:ee4a72f12847ef29b072aee9ad5474041ab2924106bdca9fcf5d7d965853e057", size = 191525, upload-time = "2026-04-10T14:26:46Z" },
{ url = "https://files.pythonhosted.org/packages/97/2a/09f70020898507a89279659a1afe3364d57fc1b2c89949081975d135f6f5/jiter-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:af72f204cf4d44258e5b4c1745130ac45ddab0e71a06333b01de660ab4187a94", size = 315502, upload-time = "2026-04-10T14:26:47.697Z" },
{ url = "https://files.pythonhosted.org/packages/d6/be/080c96a45cd74f9fce5db4fd68510b88087fb37ffe2541ff73c12db92535/jiter-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4b77da71f6e819be5fbcec11a453fde5b1d0267ef6ed487e2a392fd8e14e4e3a", size = 314870, upload-time = "2026-04-10T14:26:49.149Z" },
{ url = "https://files.pythonhosted.org/packages/7d/5e/2d0fee155826a968a832cc32438de5e2a193292c8721ca70d0b53e58245b/jiter-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f4ea612fe8b84b8b04e51d0e78029ecf3466348e25973f953de6e6a59aa4c1", size = 343406, upload-time = "2026-04-10T14:26:50.762Z" },
{ url = "https://files.pythonhosted.org/packages/70/af/bf9ee0d3a4f8dc0d679fc1337f874fe60cdbf841ebbb304b374e1c9aaceb/jiter-0.14.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62fe2451f8fcc0240261e6a4df18ecbcd58327857e61e625b2393ea3b468aac9", size = 369415, upload-time = "2026-04-10T14:26:52.188Z" },
{ url = "https://files.pythonhosted.org/packages/0f/83/8e8561eadba31f4d3948a5b712fb0447ec71c3560b57a855449e7b8ddc98/jiter-0.14.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6112f26f5afc75bcb475787d29da3aa92f9d09c7858f632f4be6ffe607be82e9", size = 461456, upload-time = "2026-04-10T14:26:53.611Z" },
{ url = "https://files.pythonhosted.org/packages/f6/c9/c5299e826a5fe6108d172b344033f61c69b1bb979dd8d9ddd4278a160971/jiter-0.14.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:215a6cb8fb7dc702aa35d475cc00ddc7f970e5c0b1417fb4b4ac5d82fa2a29db", size = 378488, upload-time = "2026-04-10T14:26:55.211Z" },
{ url = "https://files.pythonhosted.org/packages/5d/37/c16d9d15c0a471b8644b1abe3c82668092a707d9bedcf076f24ff2e380cd/jiter-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ab96a30fb3cb2c7e0cd33f7616c8860da5f5674438988a54ac717caccdbaa", size = 353242, upload-time = "2026-04-10T14:26:56.705Z" },
{ url = "https://files.pythonhosted.org/packages/58/ea/8050cb0dc654e728e1bfacbc0c640772f2181af5dedd13ae70145743a439/jiter-0.14.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3a99c1387b1f2928f799a9de899193484d66206a50e98233b6b088a7f0c1edb2", size = 356823, upload-time = "2026-04-10T14:26:58.281Z" },
{ url = "https://files.pythonhosted.org/packages/b0/3b/cf71506d270e5f84d97326bf220e47aed9b95e9a4a060758fb07772170ab/jiter-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab18d11074485438695f8d34a1b6da61db9754248f96d51341956607a8f39985", size = 392564, upload-time = "2026-04-10T14:27:00.018Z" },
{ url = "https://files.pythonhosted.org/packages/b0/cc/8c6c74a3efb5bd671bfd14f51e8a73375464ca914b1551bc3b40e26ac2c9/jiter-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:801028dcfc26ac0895e4964cbc0fd62c73be9fd4a7d7b1aaf6e5790033a719b7", size = 520322, upload-time = "2026-04-10T14:27:01.664Z" },
{ url = "https://files.pythonhosted.org/packages/41/24/68d7b883ec959884ddf00d019b2e0e82ba81b167e1253684fa90519ce33c/jiter-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ad425b087aafb4a1c7e1e98a279200743b9aaf30c3e0ba723aec93f061bd9bc8", size = 552619, upload-time = "2026-04-10T14:27:03.316Z" },
{ url = "https://files.pythonhosted.org/packages/b6/89/b1a0985223bbf3150ff9e8f46f98fc9360c1de94f48abe271bbe1b465682/jiter-0.14.0-cp313-cp313-win32.whl", hash = "sha256:882bcb9b334318e233950b8be366fe5f92c86b66a7e449e76975dfd6d776a01f", size = 205699, upload-time = "2026-04-10T14:27:04.662Z" },
{ url = "https://files.pythonhosted.org/packages/4c/19/3f339a5a7f14a11730e67f6be34f9d5105751d547b615ef593fa122a5ded/jiter-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:9b8c571a5dba09b98bd3462b5a53f27209a5cbbe85670391692ede71974e979f", size = 201323, upload-time = "2026-04-10T14:27:06.139Z" },
{ url = "https://files.pythonhosted.org/packages/50/56/752dd89c84be0e022a8ea3720bcfa0a8431db79a962578544812ce061739/jiter-0.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:34f19dcc35cb1abe7c369b3756babf8c7f04595c0807a848df8f26ef8298ef92", size = 191099, upload-time = "2026-04-10T14:27:07.564Z" },
{ url = "https://files.pythonhosted.org/packages/91/28/292916f354f25a1fe8cf2c918d1415c699a4a659ae00be0430e1c5d9ffea/jiter-0.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e89bcd7d426a75bb4952c696b267075790d854a07aad4c9894551a82c5b574ab", size = 320880, upload-time = "2026-04-10T14:27:09.326Z" },
{ url = "https://files.pythonhosted.org/packages/ad/c7/b002a7d8b8957ac3d469bd59c18ef4b1595a5216ae0de639a287b9816023/jiter-0.14.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b25beaa0d4447ea8c7ae0c18c688905d34840d7d0b937f2f7bdd52162c98a40", size = 346563, upload-time = "2026-04-10T14:27:11.287Z" },
{ url = "https://files.pythonhosted.org/packages/f9/3b/f8d07580d8706021d255a6356b8fab13ee4c869412995550ce6ed4ddf97d/jiter-0.14.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:651a8758dd413c51e3b7f6557cdc6921faf70b14106f45f969f091f5cda990ea", size = 357928, upload-time = "2026-04-10T14:27:12.729Z" },
{ url = "https://files.pythonhosted.org/packages/47/5b/ac1a974da29e35507230383110ffec59998b290a8732585d04e19a9eb5ba/jiter-0.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e1a7eead856a5038a8d291f1447176ab0b525c77a279a058121b5fccee257f6f", size = 203519, upload-time = "2026-04-10T14:27:14.125Z" },
{ url = "https://files.pythonhosted.org/packages/96/6d/9fc8433d667d2454271378a79747d8c76c10b51b482b454e6190e511f244/jiter-0.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e692633a12cda97e352fdcd1c4acc971b1c28707e1e33aeef782b0cbf051975", size = 190113, upload-time = "2026-04-10T14:27:16.638Z" },
{ url = "https://files.pythonhosted.org/packages/4f/1e/354ed92461b165bd581f9ef5150971a572c873ec3b68a916d5aa91da3cc2/jiter-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:6f396837fc7577871ca8c12edaf239ed9ccef3bbe39904ae9b8b63ce0a48b140", size = 315277, upload-time = "2026-04-10T14:27:18.109Z" },
{ url = "https://files.pythonhosted.org/packages/a6/95/8c7c7028aa8636ac21b7a55faef3e34215e6ed0cbf5ae58258427f621aa3/jiter-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a4d50ea3d8ba4176f79754333bd35f1bbcd28e91adc13eb9b7ca91bc52a6cef9", size = 315923, upload-time = "2026-04-10T14:27:19.603Z" },
{ url = "https://files.pythonhosted.org/packages/47/40/e2a852a44c4a089f2681a16611b7ce113224a80fd8504c46d78491b47220/jiter-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce17f8a050447d1b4153bda4fb7d26e6a9e74eb4f4a41913f30934c5075bf615", size = 344943, upload-time = "2026-04-10T14:27:21.262Z" },
{ url = "https://files.pythonhosted.org/packages/fc/1f/670f92adee1e9895eac41e8a4d623b6da68c4d46249d8b556b60b63f949e/jiter-0.14.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4f1c4b125e1652aefbc2e2c1617b60a160ab789d180e3d423c41439e5f32850", size = 369725, upload-time = "2026-04-10T14:27:22.766Z" },
{ url = "https://files.pythonhosted.org/packages/01/2f/541c9ba567d05de1c4874a0f8f8c5e3fd78e2b874266623da9a775cf46e0/jiter-0.14.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be808176a6a3a14321d18c603f2d40741858a7c4fc982f83232842689fe86dd9", size = 461210, upload-time = "2026-04-10T14:27:24.315Z" },
{ url = "https://files.pythonhosted.org/packages/ce/a9/c31cbec09627e0d5de7aeaec7690dba03e090caa808fefd8133137cf45bc/jiter-0.14.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26679d58ba816f88c3849306dd58cb863a90a1cf352cdd4ef67e30ccf8a77994", size = 380002, upload-time = "2026-04-10T14:27:26.155Z" },
{ url = "https://files.pythonhosted.org/packages/50/02/3c05c1666c41904a2f607475a73e7a4763d1cbde2d18229c4f85b22dc253/jiter-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80381f5a19af8fa9aef743f080e34f6b25ebd89656475f8cf0470ec6157052aa", size = 354678, upload-time = "2026-04-10T14:27:27.701Z" },
{ url = "https://files.pythonhosted.org/packages/7d/97/e15b33545c2b13518f560d695f974b9891b311641bdcf178d63177e8801e/jiter-0.14.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:004df5fdb8ecbd6d99f3227df18ba1a259254c4359736a2e6f036c944e02d7c5", size = 358920, upload-time = "2026-04-10T14:27:29.256Z" },
{ url = "https://files.pythonhosted.org/packages/ad/d2/8b1461def6b96ba44530df20d07ef7a1c7da22f3f9bf1727e2d611077bf1/jiter-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cff5708f7ed0fa098f2b53446c6fa74c48469118e5cd7497b4f1cd569ab06928", size = 394512, upload-time = "2026-04-10T14:27:31.344Z" },
{ url = "https://files.pythonhosted.org/packages/e3/88/837566dd6ed6e452e8d3205355afd484ce44b2533edfa4ed73a298ea893e/jiter-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:2492e5f06c36a976d25c7cc347a60e26d5470178d44cde1b9b75e60b4e519f28", size = 521120, upload-time = "2026-04-10T14:27:33.299Z" },
{ url = "https://files.pythonhosted.org/packages/89/6b/b00b45c4d1b4c031777fe161d620b755b5b02cdade1e316dcb46e4471d63/jiter-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7609cfbe3a03d37bfdbf5052012d5a879e72b83168a363deae7b3a26564d57de", size = 553668, upload-time = "2026-04-10T14:27:34.868Z" },
{ url = "https://files.pythonhosted.org/packages/ad/d8/6fe5b42011d19397433d345716eac16728ac241862a2aac9c91923c7509a/jiter-0.14.0-cp314-cp314-win32.whl", hash = "sha256:7282342d32e357543565286b6450378c3cd402eea333fc1ebe146f1fabb306fc", size = 207001, upload-time = "2026-04-10T14:27:36.455Z" },
{ url = "https://files.pythonhosted.org/packages/e5/43/5c2e08da1efad5e410f0eaaabeadd954812612c33fbbd8fd5328b489139d/jiter-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd77945f38866a448e73b0b7637366afa814d4617790ecd88a18ca74377e6c02", size = 202187, upload-time = "2026-04-10T14:27:38Z" },
{ url = "https://files.pythonhosted.org/packages/aa/1f/6e39ac0b4cdfa23e606af5b245df5f9adaa76f35e0c5096790da430ca506/jiter-0.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:f2d4c61da0821ee42e0cdf5489da60a6d074306313a377c2b35af464955a3611", size = 192257, upload-time = "2026-04-10T14:27:39.504Z" },
{ url = "https://files.pythonhosted.org/packages/05/57/7dbc0ffbbb5176a27e3518716608aa464aee2e2887dc938f0b900a120449/jiter-0.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1bf7ff85517dd2f20a5750081d2b75083c1b269cf75afc7511bdf1f9548beb3b", size = 323441, upload-time = "2026-04-10T14:27:41.039Z" },
{ url = "https://files.pythonhosted.org/packages/83/6e/7b3314398d8983f06b557aa21b670511ec72d3b79a68ee5e4d9bff972286/jiter-0.14.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8ef8791c3e78d6c6b157c6d360fbb5c715bebb8113bc6a9303c5caff012754a", size = 348109, upload-time = "2026-04-10T14:27:42.552Z" },
{ url = "https://files.pythonhosted.org/packages/ae/4f/8dc674bcd7db6dba566de73c08c763c337058baff1dbeb34567045b27cdc/jiter-0.14.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e74663b8b10da1fe0f4e4703fd7980d24ad17174b6bb35d8498d6e3ebce2ae6a", size = 368328, upload-time = "2026-04-10T14:27:44.574Z" },
{ url = "https://files.pythonhosted.org/packages/3b/5f/188e09a1f20906f98bbdec44ed820e19f4e8eb8aff88b9d1a5a497587ff3/jiter-0.14.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1aca29ba52913f78362ec9c2da62f22cdc4c3083313403f90c15460979b84d9b", size = 463301, upload-time = "2026-04-10T14:27:46.717Z" },
{ url = "https://files.pythonhosted.org/packages/ac/f0/19046ef965ed8f349e8554775bb12ff4352f443fbe12b95d31f575891256/jiter-0.14.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b39b7d87a952b79949af5fef44d2544e58c21a28da7f1bae3ef166455c61746", size = 378891, upload-time = "2026-04-10T14:27:48.32Z" },
{ url = "https://files.pythonhosted.org/packages/c4/c3/da43bd8431ee175695777ee78cf0e93eacbb47393ff493f18c45231b427d/jiter-0.14.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d918a68b26e9fab068c2b5453577ef04943ab2807b9a6275df2a812599a310", size = 360749, upload-time = "2026-04-10T14:27:49.88Z" },
{ url = "https://files.pythonhosted.org/packages/72/26/e054771be889707c6161dbdec9c23d33a9ec70945395d70f07cfea1e9a6f/jiter-0.14.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:b08997c35aee1201c1a5361466a8fb9162d03ae7bf6568df70b6c859f1e654a4", size = 358526, upload-time = "2026-04-10T14:27:51.504Z" },
{ url = "https://files.pythonhosted.org/packages/c3/0f/7bea65ea2a6d91f2bf989ff11a18136644392bf2b0497a1fa50934c30a9c/jiter-0.14.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:260bf7ca20704d58d41f669e5e9fe7fe2fa72901a6b324e79056f5d52e9c9be2", size = 393926, upload-time = "2026-04-10T14:27:53.368Z" },
{ url = "https://files.pythonhosted.org/packages/3c/a1/b1ff7d70deef61ac0b7c6c2f12d2ace950cdeecb4fdc94500a0926802857/jiter-0.14.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:37826e3df29e60f30a382f9294348d0238ef127f4b5d7f5f8da78b5b9e050560", size = 521052, upload-time = "2026-04-10T14:27:55.058Z" },
{ url = "https://files.pythonhosted.org/packages/0b/7b/3b0649983cbaf15eda26a414b5b1982e910c67bd6f7b1b490f3cfc76896a/jiter-0.14.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:645be49c46f2900937ba0eaf871ad5183c96858c0af74b6becc7f4e367e36e06", size = 553716, upload-time = "2026-04-10T14:27:57.269Z" },
{ url = "https://files.pythonhosted.org/packages/97/f8/33d78c83bd93ae0c0af05293a6660f88a1977caef39a6d72a84afab94ce0/jiter-0.14.0-cp314-cp314t-win32.whl", hash = "sha256:2f7877ed45118de283786178eceaf877110abacd04fde31efff3940ae9672674", size = 207957, upload-time = "2026-04-10T14:27:59.285Z" },
{ url = "https://files.pythonhosted.org/packages/d6/ac/2b760516c03e2227826d1f7025d89bf6bf6357a28fe75c2a2800873c50bf/jiter-0.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:14c0cb10337c49f5eafe8e7364daca5e29a020ea03580b8f8e6c597fed4e1588", size = 204690, upload-time = "2026-04-10T14:28:00.962Z" },
{ url = "https://files.pythonhosted.org/packages/dc/2e/a44c20c58aeed0355f2d326969a181696aeb551a25195f47563908a815be/jiter-0.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5419d4aa2024961da9fe12a9cfe7484996735dca99e8e090b5c88595ef1951ff", size = 191338, upload-time = "2026-04-10T14:28:02.853Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a0/704f5027508138f22cda277779bc6b9cace77af1a87ef93c73daf61b5b12/jiter-0.14.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:85581c4c3e4060fe3424cdfd7f3aa610f2dc5e9dde8b6863358eb68560018472", size = 319926, upload-time = "2026-04-10T14:28:04.687Z" },
{ url = "https://files.pythonhosted.org/packages/54/f6/ee3ddf36a0989959760417fbe662e4f7bc6451547a54262ef17c2882875d/jiter-0.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6279c63849444a4fe9b9abf82e5df0fc7d13dea07f53f084b362485bd1f2bbe", size = 315333, upload-time = "2026-04-10T14:28:06.248Z" },
{ url = "https://files.pythonhosted.org/packages/63/c1/ba033578d36c99455f5fbd8bbce891c29e2348dfa25811875a636975d82f/jiter-0.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59940ef6ac9f8b34c800838416f105f0503485fa8d71cae99f71d44a7285b01e", size = 349996, upload-time = "2026-04-10T14:28:07.807Z" },
{ url = "https://files.pythonhosted.org/packages/4b/fd/b6c8b38d1c53bd115abdd3143038f6f29fa4eaad915205ca959e5a16cc08/jiter-0.14.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55bee2b6a2657434984d9144c20cf27ba3b6acd495539539953e447778515efd", size = 373255, upload-time = "2026-04-10T14:28:09.597Z" },
{ url = "https://files.pythonhosted.org/packages/e4/48/153044aee4ebc7e4c92be697dd96da8e8eb7f920e1e90e4dda0c5aa7e400/jiter-0.14.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d45fc7ea86a46bd9b5bceb9e8d43e5d10a392378713fb32cf1ce851b4b0d1f8", size = 466311, upload-time = "2026-04-10T14:28:11.563Z" },
{ url = "https://files.pythonhosted.org/packages/c0/7d/2d7f2d29543f8d2960396cc44068228ef9badaac35998b255277b439d3b4/jiter-0.14.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:758d19dae7ea4c4da3cbc463dc323d1660e7353144ef17509ff43beab6da5a47", size = 382612, upload-time = "2026-04-10T14:28:13.155Z" },
{ url = "https://files.pythonhosted.org/packages/fa/8c/0522d2dac614f141111d80fb9ca1363251dcef796b21523d4a9f78684f88/jiter-0.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32959d7285d1d0deb5a8c913349e476ad9271b384f3e54cca1931c4075f54c6e", size = 361841, upload-time = "2026-04-10T14:28:14.736Z" },
{ url = "https://files.pythonhosted.org/packages/e8/48/53b5627bf5027fd9d49a35c5921e3c220140b51ef68814bfc93750c562b1/jiter-0.14.0-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:78a4c677fe5689e0e129b39f5affe9210a500b6620ebb0386ebccf5922bee9a6", size = 362913, upload-time = "2026-04-10T14:28:16.583Z" },
{ url = "https://files.pythonhosted.org/packages/7e/b5/5dde259eec978b54be123128311ee0e9eb4cc095019d06ad45401913567f/jiter-0.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ae66782ecffb1a266e1a07f5abbfc3832afdd260fc9b478982c3f8e01eba5fa", size = 399862, upload-time = "2026-04-10T14:28:18.779Z" },
{ url = "https://files.pythonhosted.org/packages/b6/0a/ef4bce553be8d9e2bbbdbdd3c0337c54773c8a9057c969242afa770d24d3/jiter-0.14.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:155dab67beac8d66cec9479c93ee2cbe7bfbc67509e5c2860e02ec2d9b0ecca1", size = 524733, upload-time = "2026-04-10T14:28:20.851Z" },
{ url = "https://files.pythonhosted.org/packages/4a/0b/e2416d657d1a9a2503f3edcb493cec5b770b8cb417ccab7ed013b56242c8/jiter-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f16b76d7d6aadbbaf7f79a76ff3a51dae14b7ebaaf9c1ba61607784ef51c537c", size = 558776, upload-time = "2026-04-10T14:28:22.85Z" },
{ url = "https://files.pythonhosted.org/packages/b8/d0/aaed54dfa1c0375df395b2fc6cf7cfb86d480c1e38b821006207774c04de/jiter-0.14.0-cp39-cp39-win32.whl", hash = "sha256:0fbad7aa06f87e8215d660fc6f05a9b07b58751a29967bbd9c81ff22d21dbe8c", size = 211145, upload-time = "2026-04-10T14:28:24.654Z" },
{ url = "https://files.pythonhosted.org/packages/0b/19/1a7be527189cdd61694cd98b83fd382e614e52053bc992728d1cda55022e/jiter-0.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:e1765c3ef3ea31fe6e282376a16def1a96f5f11a0235055696c18d9d23ff30cb", size = 206779, upload-time = "2026-04-10T14:28:26.31Z" },
{ url = "https://files.pythonhosted.org/packages/32/a1/ef34ca2cab2962598591636a1804b93645821201cc0095d4a93a9a329c9d/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a25ffa2dbbdf8721855612f6dca15c108224b12d0c4024d0ac3d7902132b4211", size = 311366, upload-time = "2026-04-10T14:28:27.943Z" },
{ url = "https://files.pythonhosted.org/packages/60/bb/520576a532a6b8a6f42747afed289c8448c879a34d7802fe2c832d4fd38f/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ac9cbaa86c10996b92bd12c91659b60f939f8e28fcfa6bc11a0e90a774ce95b", size = 309873, upload-time = "2026-04-10T14:28:29.688Z" },
{ url = "https://files.pythonhosted.org/packages/b2/7c/c16db114ea1f2f532f198aa8dc39585026af45af362c69a0492f31bc4821/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:844e73b6c56b505e9e169234ea3bdea2ea43f769f847f47ac559ba1d2361ebea", size = 344816, upload-time = "2026-04-10T14:28:31.348Z" },
{ url = "https://files.pythonhosted.org/packages/99/8f/15e7741ff19e9bcd4d753f7ff22f988fd54592f134ca13701c13ea8c20e0/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52c076f187405fc21523c746c04399c9af8ece566077ed147b2126f2bcba577", size = 351445, upload-time = "2026-04-10T14:28:33.093Z" },
{ url = "https://files.pythonhosted.org/packages/21/42/9042c3f3019de4adcb8c16591c325ec7255beea9fcd33a42a43f3b0b1000/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:fbd9e482663ca9d005d051330e4d2d8150bb208a209409c10f7e7dfdf7c49da9", size = 308810, upload-time = "2026-04-10T14:28:34.673Z" },
{ url = "https://files.pythonhosted.org/packages/60/cf/a7e19b308bd86bb04776803b1f01a5f9a287a4c55205f4708827ee487fbf/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:33a20d838b91ef376b3a56896d5b04e725c7df5bc4864cc6569cf046a8d73b6d", size = 308443, upload-time = "2026-04-10T14:28:36.658Z" },
{ url = "https://files.pythonhosted.org/packages/ca/44/e26ede3f0caeff93f222559cb0cc4ca68579f07d009d7b6010c5b586f9b1/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:432c4db5255d86a259efde91e55cb4c8d18c0521d844c9e2e7efcce3899fb016", size = 343039, upload-time = "2026-04-10T14:28:38.356Z" },
{ url = "https://files.pythonhosted.org/packages/da/e9/1f9ada30cef7b05e74bb06f52127e7a724976c225f46adb65c37b1dadfb6/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f00d94b281174144d6532a04b66a12cb866cbdc47c3af3bfe2973677f9861a", size = 349613, upload-time = "2026-04-10T14:28:40.066Z" },
]
[[package]]
name = "mytoolkit"
version = "0.1.0"
@@ -52,6 +288,7 @@ source = { editable = "." }
dependencies = [
{ name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "click", version = "8.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "openai" },
{ name = "pillow", version = "11.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "pillow", version = "12.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "pypdf" },
@@ -60,10 +297,31 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "click", specifier = ">=8.0" },
{ name = "openai", specifier = ">=1.60" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "pypdf", specifier = ">=6.10.1" },
]
[[package]]
name = "openai"
version = "2.32.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "anyio", version = "4.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "distro" },
{ name = "httpx" },
{ name = "jiter" },
{ name = "pydantic" },
{ name = "sniffio" },
{ name = "tqdm" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/59/bdcc6b759b8c42dd73afaf5bf8f902c04b37987a5514dbc1c64dba390fef/openai-2.32.0.tar.gz", hash = "sha256:c54b27a9e4cb8d51f0dd94972ffd1a04437efeb259a9e60d8922b8bd26fe55e0", size = 693286, upload-time = "2026-04-15T22:28:19.434Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/c1/d6e64ccd0536bf616556f0cad2b6d94a8125f508d25cfd814b1d2db4e2f1/openai-2.32.0-py3-none-any.whl", hash = "sha256:4dcc9badeb4bf54ad0d187453742f290226d30150890b7890711bda4f32f192f", size = 1162570, upload-time = "2026-04-15T22:28:17.714Z" },
]
[[package]]
name = "pillow"
version = "11.3.0"
@@ -281,6 +539,151 @@ wheels = [
{ 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" },
]
[[package]]
name = "pydantic"
version = "2.13.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" },
]
[[package]]
name = "pydantic-core"
version = "2.46.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/98/b50eb9a411e87483b5c65dba4fa430a06bac4234d3403a40e5a9905ebcd0/pydantic_core-2.46.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1", size = 2108971, upload-time = "2026-04-20T14:43:51.945Z" },
{ url = "https://files.pythonhosted.org/packages/08/4b/f364b9d161718ff2217160a4b5d41ce38de60aed91c3689ebffa1c939d23/pydantic_core-2.46.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f", size = 1949588, upload-time = "2026-04-20T14:44:10.386Z" },
{ url = "https://files.pythonhosted.org/packages/8f/8b/30bd03ee83b2f5e29f5ba8e647ab3c456bf56f2ec72fdbcc0215484a0854/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3", size = 1975986, upload-time = "2026-04-20T14:43:57.106Z" },
{ url = "https://files.pythonhosted.org/packages/3c/54/13ccf954d84ec275d5d023d5786e4aa48840bc9f161f2838dc98e1153518/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a", size = 2055830, upload-time = "2026-04-20T14:44:15.499Z" },
{ url = "https://files.pythonhosted.org/packages/be/0e/65f38125e660fdbd72aa858e7dfae893645cfa0e7b13d333e174a367cd23/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807", size = 2222340, upload-time = "2026-04-20T14:41:51.353Z" },
{ url = "https://files.pythonhosted.org/packages/d1/88/f3ab7739efe0e7e80777dbb84c59eb98518e3f57ea433206194c2e425272/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda", size = 2280727, upload-time = "2026-04-20T14:41:30.461Z" },
{ url = "https://files.pythonhosted.org/packages/2a/6d/c228219080817bec4982f9531cadb18da6aaa770fdeb114f49c237ac2c9f/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57", size = 2092158, upload-time = "2026-04-20T14:44:07.305Z" },
{ url = "https://files.pythonhosted.org/packages/0f/b1/525a16711e7c6d61635fac3b0bd54600b5c5d9f60c6fc5aaab26b64a2297/pydantic_core-2.46.3-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045", size = 2116626, upload-time = "2026-04-20T14:42:34.118Z" },
{ url = "https://files.pythonhosted.org/packages/ef/7c/17d30673351439a6951bf54f564cf2443ab00ae264ec9df00e2efd710eb5/pydantic_core-2.46.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943", size = 2160691, upload-time = "2026-04-20T14:41:14.023Z" },
{ url = "https://files.pythonhosted.org/packages/86/66/af8adbcbc0886ead7f1a116606a534d75a307e71e6e08226000d51b880d2/pydantic_core-2.46.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f", size = 2182543, upload-time = "2026-04-20T14:40:48.886Z" },
{ url = "https://files.pythonhosted.org/packages/b0/37/6de71e0f54c54a4190010f57deb749e1ddf75c568ada3b1320b70067f121/pydantic_core-2.46.3-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4", size = 2324513, upload-time = "2026-04-20T14:42:36.121Z" },
{ url = "https://files.pythonhosted.org/packages/51/b1/9fc74ce94f603d5ef59ff258ca9c2c8fb902fb548d340a96f77f4d1c3b7f/pydantic_core-2.46.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a", size = 2361853, upload-time = "2026-04-20T14:43:24.886Z" },
{ url = "https://files.pythonhosted.org/packages/40/d0/4c652fc592db35f100279ee751d5a145aca1b9a7984b9684ba7c1b5b0535/pydantic_core-2.46.3-cp310-cp310-win32.whl", hash = "sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7", size = 1980465, upload-time = "2026-04-20T14:44:46.239Z" },
{ url = "https://files.pythonhosted.org/packages/27/b8/a920453c38afbe1f355e1ea0b0d94a0a3e0b0879d32d793108755fa171d5/pydantic_core-2.46.3-cp310-cp310-win_amd64.whl", hash = "sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6", size = 2073884, upload-time = "2026-04-20T14:43:01.201Z" },
{ url = "https://files.pythonhosted.org/packages/22/a2/1ba90a83e85a3f94c796b184f3efde9c72f2830dcda493eea8d59ba78e6d/pydantic_core-2.46.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5", size = 2106740, upload-time = "2026-04-20T14:41:20.932Z" },
{ url = "https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c", size = 1948293, upload-time = "2026-04-20T14:43:42.115Z" },
{ url = "https://files.pythonhosted.org/packages/3e/b8/2e8e636dc9e3f16c2e16bf0849e24be82c5ee82c603c65fc0326666328fc/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e", size = 1973222, upload-time = "2026-04-20T14:41:57.841Z" },
{ url = "https://files.pythonhosted.org/packages/34/36/0e730beec4d83c5306f417afbd82ff237d9a21e83c5edf675f31ed84c1fe/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287", size = 2053852, upload-time = "2026-04-20T14:40:43.077Z" },
{ url = "https://files.pythonhosted.org/packages/4b/f0/3071131f47e39136a17814576e0fada9168569f7f8c0e6ac4d1ede6a4958/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe", size = 2221134, upload-time = "2026-04-20T14:43:03.349Z" },
{ url = "https://files.pythonhosted.org/packages/2f/a9/a2dc023eec5aa4b02a467874bad32e2446957d2adcab14e107eab502e978/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050", size = 2279785, upload-time = "2026-04-20T14:41:19.285Z" },
{ url = "https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2", size = 2089404, upload-time = "2026-04-20T14:43:10.108Z" },
{ url = "https://files.pythonhosted.org/packages/2a/78/8692e3aa72b2d004f7a5d937f1dfdc8552ba26caf0bec75f342c40f00dec/pydantic_core-2.46.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa", size = 2114898, upload-time = "2026-04-20T14:44:51.475Z" },
{ url = "https://files.pythonhosted.org/packages/6a/62/e83133f2e7832532060175cebf1f13748f4c7e7e7165cdd1f611f174494b/pydantic_core-2.46.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c", size = 2157856, upload-time = "2026-04-20T14:43:46.64Z" },
{ url = "https://files.pythonhosted.org/packages/6d/ec/6a500e3ad7718ee50583fae79c8651f5d37e3abce1fa9ae177ae65842c53/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf", size = 2180168, upload-time = "2026-04-20T14:42:00.302Z" },
{ url = "https://files.pythonhosted.org/packages/d8/53/8267811054b1aa7fc1dc7ded93812372ef79a839f5e23558136a6afbfde1/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b", size = 2322885, upload-time = "2026-04-20T14:41:05.253Z" },
{ url = "https://files.pythonhosted.org/packages/c8/c1/1c0acdb3aa0856ddc4ecc55214578f896f2de16f400cf51627eb3c26c1c4/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e", size = 2360328, upload-time = "2026-04-20T14:41:43.991Z" },
{ url = "https://files.pythonhosted.org/packages/f0/d0/ef39cd0f4a926814f360e71c1adeab48ad214d9727e4deb48eedfb5bce1a/pydantic_core-2.46.3-cp311-cp311-win32.whl", hash = "sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb", size = 1979464, upload-time = "2026-04-20T14:43:12.215Z" },
{ url = "https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl", hash = "sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346", size = 2070837, upload-time = "2026-04-20T14:41:47.707Z" },
{ url = "https://files.pythonhosted.org/packages/9f/1e/264a17cd582f6ed50950d4d03dd5fefd84e570e238afe1cb3e25cf238769/pydantic_core-2.46.3-cp311-cp311-win_arm64.whl", hash = "sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6", size = 2053647, upload-time = "2026-04-20T14:42:27.535Z" },
{ url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" },
{ url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" },
{ url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" },
{ url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" },
{ url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" },
{ url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" },
{ url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" },
{ url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" },
{ url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" },
{ url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" },
{ url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" },
{ url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" },
{ url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" },
{ url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" },
{ url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" },
{ url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" },
{ url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" },
{ url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" },
{ url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" },
{ url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" },
{ url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" },
{ url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" },
{ url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" },
{ url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" },
{ url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" },
{ url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" },
{ url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" },
{ url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" },
{ url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" },
{ url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" },
{ url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" },
{ url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" },
{ url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" },
{ url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" },
{ url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" },
{ url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" },
{ url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" },
{ url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" },
{ url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" },
{ url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" },
{ url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" },
{ url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" },
{ url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" },
{ url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" },
{ url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" },
{ url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" },
{ url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" },
{ url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" },
{ url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" },
{ url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" },
{ url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" },
{ url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" },
{ url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" },
{ url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" },
{ url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" },
{ url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" },
{ url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" },
{ url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" },
{ url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" },
{ url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" },
{ url = "https://files.pythonhosted.org/packages/31/75/c1ee7cb5b2277fe1f95837a58fb692eeda7162494fc1ddccb5f23be1d7f6/pydantic_core-2.46.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb", size = 2112012, upload-time = "2026-04-20T14:44:35.697Z" },
{ url = "https://files.pythonhosted.org/packages/6b/da/fb883281703dc5f4d68cdc648c503c46c8af5726f428013178d657c75181/pydantic_core-2.46.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495", size = 1953006, upload-time = "2026-04-20T14:43:33.878Z" },
{ url = "https://files.pythonhosted.org/packages/92/47/5d7d7d04204920771086d768ca51306e34b63a31ad80a5f82c9d38dea568/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873", size = 1980050, upload-time = "2026-04-20T14:41:03.154Z" },
{ url = "https://files.pythonhosted.org/packages/e0/e9/1c52019dd95babcff1bffe40022e8541c0447b8e4a44596a979c7309fa0c/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0", size = 2057165, upload-time = "2026-04-20T14:43:44.507Z" },
{ url = "https://files.pythonhosted.org/packages/6f/0e/e0aac1f35baf80ff8f35d4fa58d85dce248324a764d950460cf5c0569758/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d", size = 2225133, upload-time = "2026-04-20T14:41:07.334Z" },
{ url = "https://files.pythonhosted.org/packages/d5/df/8eb296b9661574ef26e990856991615c1e1fb1744a10b896bff684a3c99a/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a", size = 2282409, upload-time = "2026-04-20T14:43:07.567Z" },
{ url = "https://files.pythonhosted.org/packages/d2/07/888c53b769be71072a1dbd741ec25160579d789ef276fd5bd2045a07b09a/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168", size = 2095775, upload-time = "2026-04-20T14:42:21.586Z" },
{ url = "https://files.pythonhosted.org/packages/8a/19/6c32bdb93362b899bf92ac42c824898ab93dd8a23f339cbf5c5098ef61a6/pydantic_core-2.46.3-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c", size = 2118999, upload-time = "2026-04-20T14:44:43.791Z" },
{ url = "https://files.pythonhosted.org/packages/6b/c6/31d926d003f59a032ff61b62990e378f0fb985aa9d77fd448a6140f7eb5f/pydantic_core-2.46.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720", size = 2162368, upload-time = "2026-04-20T14:41:12.529Z" },
{ url = "https://files.pythonhosted.org/packages/4f/a1/48d669051947845ff54f5b7cb6254ccafb23aa284184de6f273145d0e45b/pydantic_core-2.46.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6", size = 2184786, upload-time = "2026-04-20T14:42:40.235Z" },
{ url = "https://files.pythonhosted.org/packages/08/ff/a722cade3d4bd8ed6433a8e84eddf2f993b5fb4a47cade269289cfb4cc0c/pydantic_core-2.46.3-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd", size = 2325626, upload-time = "2026-04-20T14:41:37.73Z" },
{ url = "https://files.pythonhosted.org/packages/ad/8a/238e33805d6f3691bcce8739db7d0376a2388e1b3f2c8db2128f6683fc78/pydantic_core-2.46.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd", size = 2363286, upload-time = "2026-04-20T14:42:29.385Z" },
{ url = "https://files.pythonhosted.org/packages/ad/2e/ab044f4431798a565242d5e8e48d080faa931f45e3d30df62e4f501d9bb0/pydantic_core-2.46.3-cp39-cp39-win32.whl", hash = "sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79", size = 1981660, upload-time = "2026-04-20T14:43:54.525Z" },
{ url = "https://files.pythonhosted.org/packages/d0/2c/13855786276f51fe86915c0533f4fc14e1d5421726ba8ad57caa09eb18ec/pydantic_core-2.46.3-cp39-cp39-win_amd64.whl", hash = "sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13", size = 2078793, upload-time = "2026-04-20T14:42:17.87Z" },
{ url = "https://files.pythonhosted.org/packages/66/7f/03dbad45cd3aa9083fbc93c210ae8b005af67e4136a14186950a747c6874/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46", size = 2105683, upload-time = "2026-04-20T14:42:19.779Z" },
{ url = "https://files.pythonhosted.org/packages/26/22/4dc186ac8ea6b257e9855031f51b62a9637beac4d68ac06bee02f046f836/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874", size = 1940052, upload-time = "2026-04-20T14:43:59.274Z" },
{ url = "https://files.pythonhosted.org/packages/0d/ca/d376391a5aff1f2e8188960d7873543608130a870961c2b6b5236627c116/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76", size = 1988172, upload-time = "2026-04-20T14:41:17.469Z" },
{ url = "https://files.pythonhosted.org/packages/0e/6b/523b9f85c23788755d6ab949329de692a2e3a584bc6beb67fef5e035aa9d/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531", size = 2128596, upload-time = "2026-04-20T14:40:41.707Z" },
{ url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" },
{ url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" },
{ url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" },
{ url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" },
{ url = "https://files.pythonhosted.org/packages/1f/da/99d40830684f81dec901cac521b5b91c095394cc1084b9433393cde1c2df/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25", size = 2107973, upload-time = "2026-04-20T14:42:06.175Z" },
{ url = "https://files.pythonhosted.org/packages/99/a5/87024121818d75bbb2a98ddbaf638e40e7a18b5e0f5492c9ca4b1b316107/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3", size = 1947191, upload-time = "2026-04-20T14:43:14.319Z" },
{ url = "https://files.pythonhosted.org/packages/60/62/0c1acfe10945b83a6a59d19fbaa92f48825381509e5701b855c08f13db76/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536", size = 2123791, upload-time = "2026-04-20T14:43:22.766Z" },
{ url = "https://files.pythonhosted.org/packages/75/3e/3b2393b4c8f44285561dc30b00cf307a56a2eff7c483a824db3b8221ca51/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1", size = 2153197, upload-time = "2026-04-20T14:44:27.932Z" },
{ url = "https://files.pythonhosted.org/packages/ba/75/5af02fb35505051eee727c061f2881c555ab4f8ddb2d42da715a42c9731b/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c", size = 2181073, upload-time = "2026-04-20T14:43:20.729Z" },
{ url = "https://files.pythonhosted.org/packages/10/92/7e0e1bd9ca3c68305db037560ca2876f89b2647deb2f8b6319005de37505/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85", size = 2315886, upload-time = "2026-04-20T14:44:04.826Z" },
{ url = "https://files.pythonhosted.org/packages/b8/d8/101655f27eaf3e44558ead736b2795d12500598beed4683f279396fa186e/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8", size = 2360528, upload-time = "2026-04-20T14:40:47.431Z" },
{ url = "https://files.pythonhosted.org/packages/07/0f/1c34a74c8d07136f0d729ffe5e1fdab04fbdaa7684f61a92f92511a84a15/pydantic_core-2.46.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff", size = 2184144, upload-time = "2026-04-20T14:42:57Z" },
]
[[package]]
name = "pypdf"
version = "6.10.1"
@@ -293,6 +696,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/04/e3aa7f1f14dbc53429cae34666261eb935d99bd61d24756ab94d7e0309da/pypdf-6.10.1-py3-none-any.whl", hash = "sha256:6331940d3bfe75b7e6601d35db7adabab5fc1d716efaeb384e3c0c3957d033de", size = 335606, upload-time = "2026-04-14T12:55:18.941Z" },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
]
[[package]]
name = "tqdm"
version = "4.67.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
@@ -301,3 +725,15 @@ sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac8
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
]