Rename Castle -> Wild PC across the repo
Repo-side rename only (Phases 1-3 of the migration plan); the live box (~/.castle, systemd units, /data/castle, domains) is a separate cutover. - Slug `castle` -> `wildpc`: CLI command, module names (wildpc_core/cli/api), dist names, entry point `wildpc = wildpc_cli.main:main`. - Identifiers: CastleConfig/NATSClient/DirError/MDNS -> Wildpc*. - Env/constants: CASTLE_* -> WILDPC_*; ~/.castle -> ~/.wildpc, castle.yaml -> wildpc.yaml, /data/castle -> /data/wildpc. - Systemd UNIT_PREFIX castle- -> wildpc-; own programs castle-api/gateway/etc. - Display prose "Castle" -> "Wild PC" in docs, agent-guide files, README, frontend. - Package dirs and bootstrap yaml renamed via git mv; lockfiles regenerated; redundant nested uv.lock files dropped (workspace root lock is authoritative). Tests: core 273, cli 47, wildpc-api 120 all pass. Frontend type-checks + builds. Fixed a stale test fixture (secret_env_path kind arg) broken pre-rename.
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
[project]
|
||||
name = "castle-cli"
|
||||
name = "wildpc-cli"
|
||||
version = "0.1.0"
|
||||
description = "Castle platform CLI - manage projects, services, and infrastructure"
|
||||
description = "Wild PC platform CLI - manage projects, services, and infrastructure"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"pyyaml>=6.0.0",
|
||||
"jinja2>=3.1.0",
|
||||
"pydantic>=2.0.0",
|
||||
"castle-core",
|
||||
"wildpc-core",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
castle-core = { workspace = true }
|
||||
wildpc-core = { workspace = true }
|
||||
|
||||
[project.scripts]
|
||||
castle = "castle_cli.main:main"
|
||||
wildpc = "wildpc_cli.main:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/castle_cli"]
|
||||
packages = ["src/wildpc_cli"]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
"""Castle CLI - manage projects, services, and infrastructure."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
3
cli/src/wildpc_cli/__init__.py
Normal file
3
cli/src/wildpc_cli/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""Wild PC CLI - manage projects, services, and infrastructure."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
@@ -1,10 +1,10 @@
|
||||
"""castle program add — adopt an existing repo as a program (no scaffolding).
|
||||
"""wildpc program add — adopt an existing repo as a program (no scaffolding).
|
||||
|
||||
`castle program create` makes new code from a stack. `castle program add` adopts code that
|
||||
`wildpc program create` makes new code from a stack. `wildpc program add` adopts code that
|
||||
already exists — a local path, or a git URL to clone. It detects sensible dev
|
||||
verb commands so a non-castle project becomes usable without writing them by hand.
|
||||
verb commands so a non-wildpc project becomes usable without writing them by hand.
|
||||
|
||||
The adopt logic itself lives in ``castle_core.adopt`` so the CLI and the API
|
||||
The adopt logic itself lives in ``wildpc_core.adopt`` so the CLI and the API
|
||||
(`POST /programs/adopt`, the dashboard's "Add program") behave identically.
|
||||
"""
|
||||
|
||||
@@ -12,9 +12,9 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
|
||||
from castle_core.adopt import AdoptError, build_adopted_program
|
||||
from wildpc_core.adopt import AdoptError, build_adopted_program
|
||||
|
||||
from castle_cli.config import load_config, save_config
|
||||
from wildpc_cli.config import load_config, save_config
|
||||
|
||||
|
||||
def run_add(args: argparse.Namespace) -> int:
|
||||
@@ -35,11 +35,11 @@ def run_add(args: argparse.Namespace) -> int:
|
||||
print(f"Adopted '{adopted.name}' as a program.")
|
||||
print(f" source: {adopted.source}")
|
||||
if adopted.repo:
|
||||
print(f" repo: {adopted.repo} (run 'castle clone {adopted.name}' to fetch it)")
|
||||
print(f" repo: {adopted.repo} (run 'wildpc clone {adopted.name}' to fetch it)")
|
||||
if adopted.stack:
|
||||
print(f" stack: {adopted.stack} (verbs inherited from stack defaults)")
|
||||
elif adopted.commands:
|
||||
print(f" commands detected: {', '.join(adopted.commands)}")
|
||||
else:
|
||||
print(" no stack/commands detected — declare verbs in castle.yaml as needed")
|
||||
print(" no stack/commands detected — declare verbs in wildpc.yaml as needed")
|
||||
return 0
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle apply — converge the running system to match config.
|
||||
"""wildpc apply — converge the running system to match config.
|
||||
|
||||
The one workhorse verb: renders systemd units + the Caddyfile + tunnel config,
|
||||
then reconciles the runtime (activate what's enabled and down, restart what
|
||||
@@ -28,7 +28,7 @@ def _line(verb_color: str, verb: str, names: list[str]) -> None:
|
||||
|
||||
|
||||
def run_apply(args: argparse.Namespace) -> int:
|
||||
from castle_core.deploy import apply
|
||||
from wildpc_core.deploy import apply
|
||||
|
||||
target = getattr(args, "name", None)
|
||||
plan = getattr(args, "plan", False)
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle clone — clone source for programs that declare a `repo:` URL.
|
||||
"""wildpc clone — clone source for programs that declare a `repo:` URL.
|
||||
|
||||
Used to provision a fresh machine: every program with a `repo:` and a missing
|
||||
local `source:` gets cloned. A program whose source already exists is skipped.
|
||||
@@ -10,7 +10,7 @@ import argparse
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
def _clone_one(name: str, repo: str, source: str | None, ref: str | None, repos_dir: Path) -> bool:
|
||||
@@ -1,12 +1,12 @@
|
||||
"""castle program create — scaffold a new program from templates."""
|
||||
"""wildpc program create — scaffold a new program from templates."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
from castle_cli.config import load_config, save_config
|
||||
from castle_cli.manifest import (
|
||||
from wildpc_cli.config import load_config, save_config
|
||||
from wildpc_cli.manifest import (
|
||||
BuildSpec,
|
||||
CaddyDeployment,
|
||||
DefaultsSpec,
|
||||
@@ -21,7 +21,7 @@ from castle_cli.manifest import (
|
||||
SystemdDeployment,
|
||||
SystemdSpec,
|
||||
)
|
||||
from castle_cli.templates.scaffold import scaffold_project
|
||||
from wildpc_cli.templates.scaffold import scaffold_project
|
||||
|
||||
# Stack determines the default deployment kind + scaffold template.
|
||||
STACK_DEFAULTS: dict[str, str] = {
|
||||
@@ -79,7 +79,7 @@ def run_create(args: argparse.Namespace) -> int:
|
||||
kind = STACK_DEFAULTS.get(stack) if stack else None
|
||||
|
||||
if name in config.programs or config.deployments_named(name):
|
||||
print(f"Error: '{name}' already exists in castle.yaml")
|
||||
print(f"Error: '{name}' already exists in wildpc.yaml")
|
||||
return 1
|
||||
|
||||
config.repos_dir.mkdir(parents=True, exist_ok=True)
|
||||
@@ -94,7 +94,7 @@ def run_create(args: argparse.Namespace) -> int:
|
||||
port = next_available_port(config)
|
||||
|
||||
package_name = name.replace("-", "_")
|
||||
description = args.description or (f"A castle {stack} program" if stack else f"{name}")
|
||||
description = args.description or (f"A wildpc {stack} program" if stack else f"{name}")
|
||||
|
||||
if stack:
|
||||
scaffold_project(
|
||||
@@ -168,7 +168,7 @@ def run_create(args: argparse.Namespace) -> int:
|
||||
requires=seeded_requires,
|
||||
proxy=True, # expose at <name>.<gateway.domain>
|
||||
manage=ManageSpec(systemd=SystemdSpec()),
|
||||
# python-fastapi scaffold reads env_prefix MY_SERVICE_ — map castle's
|
||||
# python-fastapi scaffold reads env_prefix MY_SERVICE_ — map wildpc's
|
||||
# computed port/data dir to those vars (explicit, no hidden injection).
|
||||
defaults=DefaultsSpec(
|
||||
env={f"{prefix}_PORT": "${port}", f"{prefix}_DATA_DIR": "${data_dir}"}
|
||||
@@ -181,24 +181,24 @@ def run_create(args: argparse.Namespace) -> int:
|
||||
print(f"Created {label} '{name}' at {project_dir}")
|
||||
if port:
|
||||
print(f" Port: {port}")
|
||||
print(" Registered in castle.yaml")
|
||||
print(" Registered in wildpc.yaml")
|
||||
print("\nNext steps:")
|
||||
print(f" cd {project_dir}")
|
||||
if stack == "supabase":
|
||||
print(" # edit migrations/, functions/, public/ — targets the shared substrate")
|
||||
print(f" castle program build {name} # apply migrations to the substrate")
|
||||
print(f" castle apply # serve at {name}.<gateway.domain>")
|
||||
print(f" wildpc program build {name} # apply migrations to the substrate")
|
||||
print(f" wildpc apply # serve at {name}.<gateway.domain>")
|
||||
elif stack == "hugo":
|
||||
print(" # edit content/, layouts/ (or add a theme under themes/)")
|
||||
print(f" castle program build {name} # hugo --gc --minify -> public/")
|
||||
print(f" castle apply {name} # serve at {name}.<gateway.domain>")
|
||||
print(f" wildpc program build {name} # hugo --gc --minify -> public/")
|
||||
print(f" wildpc apply {name} # serve at {name}.<gateway.domain>")
|
||||
elif stack:
|
||||
print(" uv sync")
|
||||
if kind == "service":
|
||||
print(f" uv run {name} # starts on port {port}")
|
||||
print(f" castle apply {name}")
|
||||
print(f" castle test {name}")
|
||||
print(f" wildpc apply {name}")
|
||||
print(f" wildpc test {name}")
|
||||
else:
|
||||
print(" # add code, then declare commands: in castle.yaml")
|
||||
print(" # add code, then declare commands: in wildpc.yaml")
|
||||
|
||||
return 0
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle delete — remove a program/deployment from the registry AND tear it down.
|
||||
"""wildpc delete — remove a program/deployment from the registry AND tear it down.
|
||||
|
||||
Deleting cascades: a program's referencing deployments are taken offline (stop +
|
||||
disable a service/job, uninstall a tool from PATH, drop a static route) before the
|
||||
@@ -18,7 +18,7 @@ import argparse
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from castle_cli.config import load_config, save_config
|
||||
from wildpc_cli.config import load_config, save_config
|
||||
|
||||
|
||||
def run_delete(args: argparse.Namespace) -> int:
|
||||
@@ -35,7 +35,7 @@ def run_delete(args: argparse.Namespace) -> int:
|
||||
in_deployment = bool(named) and resource in _DEPLOY_RESOURCES
|
||||
if not (in_programs or in_deployment):
|
||||
where = f" {resource}" if resource else ""
|
||||
print(f"Error: no{where} '{name}' in castle.yaml")
|
||||
print(f"Error: no{where} '{name}' in wildpc.yaml")
|
||||
return 1
|
||||
|
||||
where = [
|
||||
@@ -60,7 +60,7 @@ def run_delete(args: argparse.Namespace) -> int:
|
||||
# entry itself — its stack may own persistent data (a DB schema) that survives
|
||||
# a code delete unless --purge-data drops it via the stack's `teardown`.
|
||||
public_hosts = _public_hosts(config, deployments_to_remove)
|
||||
from castle_core.stacks import get_handler
|
||||
from wildpc_core.stacks import get_handler
|
||||
|
||||
program_spec = config.programs.get(name) if in_programs else None
|
||||
stack = program_spec.stack if program_spec else None
|
||||
@@ -73,7 +73,7 @@ def run_delete(args: argparse.Namespace) -> int:
|
||||
source_dir = Path(config.programs[name].source)
|
||||
|
||||
purge_data = getattr(args, "purge_data", False)
|
||||
print(f"Will remove '{name}' from castle.yaml ({', '.join(where)}).")
|
||||
print(f"Will remove '{name}' from wildpc.yaml ({', '.join(where)}).")
|
||||
if deployments_to_remove:
|
||||
print(
|
||||
"Will tear down deployment(s): "
|
||||
@@ -100,7 +100,7 @@ def run_delete(args: argparse.Namespace) -> int:
|
||||
if deployments_to_remove:
|
||||
import asyncio
|
||||
|
||||
from castle_core.lifecycle import deactivate
|
||||
from wildpc_core.lifecycle import deactivate
|
||||
|
||||
for kind, d in deployments_to_remove:
|
||||
try:
|
||||
@@ -114,18 +114,18 @@ def run_delete(args: argparse.Namespace) -> int:
|
||||
if in_programs:
|
||||
del config.programs[name]
|
||||
save_config(config)
|
||||
print(f"Removed '{name}' from castle.yaml ({', '.join(where)}).")
|
||||
print(f"Removed '{name}' from wildpc.yaml ({', '.join(where)}).")
|
||||
|
||||
# Converge the runtime: prune orphan units, regenerate the Caddyfile (dropping
|
||||
# the static route), reload the gateway + tunnel.
|
||||
if deployments_to_remove:
|
||||
from castle_core.deploy import deploy
|
||||
from wildpc_core.deploy import deploy
|
||||
|
||||
try:
|
||||
deploy()
|
||||
print("Reconciled runtime (castle apply).")
|
||||
print("Reconciled runtime (wildpc apply).")
|
||||
except Exception as e:
|
||||
print(f"warning: reconcile failed — run 'castle apply': {e}")
|
||||
print(f"warning: reconcile failed — run 'wildpc apply': {e}")
|
||||
|
||||
# Optional: delete the source directory.
|
||||
if args.source and source_dir:
|
||||
@@ -1,6 +1,6 @@
|
||||
"""castle service create / castle job create — declare a deployment.
|
||||
"""wildpc service create / wildpc job create — declare a deployment.
|
||||
|
||||
A service or job can run anything (a castle program or not). `--program`
|
||||
A service or job can run anything (a wildpc program or not). `--program`
|
||||
records a convenience reference for description fallthrough; the run target is
|
||||
the console script (python) or argv (command) to execute.
|
||||
"""
|
||||
@@ -9,8 +9,8 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
|
||||
from castle_cli.config import load_config, save_config
|
||||
from castle_cli.manifest import (
|
||||
from wildpc_cli.config import load_config, save_config
|
||||
from wildpc_cli.manifest import (
|
||||
DefaultsSpec,
|
||||
ExposeSpec,
|
||||
HttpExposeSpec,
|
||||
@@ -49,7 +49,7 @@ def _check_new(config: object, name: str, label: str) -> str | None:
|
||||
|
||||
|
||||
def run_service_create(args: argparse.Namespace) -> int:
|
||||
"""Create a service entry in castle.yaml."""
|
||||
"""Create a service entry in wildpc.yaml."""
|
||||
config = load_config()
|
||||
name = args.name
|
||||
if err := _check_new(config, name, "service"):
|
||||
@@ -89,12 +89,12 @@ def run_service_create(args: argparse.Namespace) -> int:
|
||||
print(f" port: {args.port}")
|
||||
if reach != Reach.OFF:
|
||||
print(f" subdomain: {name}.<gateway.domain>")
|
||||
print(f"\nNext: castle apply {name}")
|
||||
print(f"\nNext: wildpc apply {name}")
|
||||
return 0
|
||||
|
||||
|
||||
def run_job_create(args: argparse.Namespace) -> int:
|
||||
"""Create a job entry in castle.yaml."""
|
||||
"""Create a job entry in wildpc.yaml."""
|
||||
config = load_config()
|
||||
name = args.name
|
||||
if err := _check_new(config, name, "job"):
|
||||
@@ -119,5 +119,5 @@ def run_job_create(args: argparse.Namespace) -> int:
|
||||
print(f"Created job '{name}'.")
|
||||
print(f" runs: {args.launcher} ({args.run or args.program or name})")
|
||||
print(f" schedule: {args.schedule}")
|
||||
print(f"\nNext: castle apply {name}")
|
||||
print(f"\nNext: wildpc apply {name}")
|
||||
return 0
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle build/test/lint/type-check/check/run/install/uninstall — dev verbs.
|
||||
"""wildpc build/test/lint/type-check/check/run/install/uninstall — dev verbs.
|
||||
|
||||
Verbs resolve per-program: a declared command (manifest `commands:` / `build:`)
|
||||
overrides the stack default, falling back to the stack handler, else unavailable.
|
||||
@@ -9,12 +9,12 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import asyncio
|
||||
|
||||
from castle_core.stacks import is_available, run_action
|
||||
from wildpc_core.stacks import is_available, run_action
|
||||
|
||||
from castle_cli.config import CastleConfig, load_config
|
||||
from wildpc_cli.config import WildpcConfig, load_config
|
||||
|
||||
|
||||
def _run_verb(config: CastleConfig, project_name: str, verb: str) -> bool:
|
||||
def _run_verb(config: WildpcConfig, project_name: str, verb: str) -> bool:
|
||||
"""Run a verb for a single program. Returns True on success."""
|
||||
if project_name not in config.programs:
|
||||
print(f"Unknown program: {project_name}")
|
||||
@@ -39,7 +39,7 @@ def _run_verb(config: CastleConfig, project_name: str, verb: str) -> bool:
|
||||
return result.status == "ok"
|
||||
|
||||
|
||||
def _run_verb_all(config: CastleConfig, verb: str) -> bool:
|
||||
def _run_verb_all(config: WildpcConfig, verb: str) -> bool:
|
||||
"""Run a verb across every program that supports it. Returns True if all pass."""
|
||||
all_passed = True
|
||||
ran_any = False
|
||||
@@ -89,10 +89,10 @@ def run_check(args: argparse.Namespace) -> int:
|
||||
return run_verb(args, "check")
|
||||
|
||||
|
||||
def _lifecycle(config: CastleConfig, name: str, deactivate: bool) -> bool:
|
||||
def _lifecycle(config: WildpcConfig, name: str, deactivate: bool) -> bool:
|
||||
"""Activate or deactivate a single program. Returns True on success."""
|
||||
from castle_core.lifecycle import activate
|
||||
from castle_core.lifecycle import deactivate as do_deactivate
|
||||
from wildpc_core.lifecycle import activate
|
||||
from wildpc_core.lifecycle import deactivate as do_deactivate
|
||||
|
||||
if name not in config.programs and name not in config.services and name not in config.jobs:
|
||||
print(f"Unknown program: {name}")
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle doctor — diagnose whether this node is set up and running.
|
||||
"""wildpc doctor — diagnose whether this node is set up and running.
|
||||
|
||||
Read-only. It answers the question the runtime status view can't: *is this node
|
||||
correctly configured, and if not, what's the exact next command?* Runs a series
|
||||
@@ -6,7 +6,7 @@ of checks grouped into Environment, Configuration, Runtime, and TLS & exposure;
|
||||
each check reports ok / warn / fail with a one-line hint when action is needed.
|
||||
|
||||
Exit code: 0 when nothing FAILed (warnings are allowed), 1 otherwise — so it
|
||||
doubles as a scriptable smoke test after `./install.sh` or `castle apply`.
|
||||
doubles as a scriptable smoke test after `./install.sh` or `wildpc apply`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -26,9 +26,9 @@ _ICON = {
|
||||
FAIL: "\033[31m✗\033[0m",
|
||||
}
|
||||
|
||||
_GATEWAY = "castle-gateway"
|
||||
_API = "castle-api"
|
||||
_DASHBOARD = "castle"
|
||||
_GATEWAY = "wildpc-gateway"
|
||||
_API = "wildpc-api"
|
||||
_DASHBOARD = "wildpc"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -62,13 +62,13 @@ def _port_open(port: int, host: str = "127.0.0.1") -> bool:
|
||||
def _check_environment() -> list[Check]:
|
||||
checks: list[Check] = []
|
||||
|
||||
if shutil.which("castle"):
|
||||
checks.append(Check(OK, "castle CLI on PATH"))
|
||||
if shutil.which("wildpc"):
|
||||
checks.append(Check(OK, "wildpc CLI on PATH"))
|
||||
else:
|
||||
checks.append(
|
||||
Check(
|
||||
WARN,
|
||||
"castle CLI not on PATH",
|
||||
"wildpc CLI not on PATH",
|
||||
hint="ensure ~/.local/bin is on PATH (uv tool install target)",
|
||||
)
|
||||
)
|
||||
@@ -119,7 +119,7 @@ def _check_configuration(config) -> list[Check]:
|
||||
checks.append(
|
||||
Check(
|
||||
OK,
|
||||
"castle.yaml loaded",
|
||||
"wildpc.yaml loaded",
|
||||
detail=f"gateway :{gw.port}, tls={tls}"
|
||||
+ (f", domain={gw.domain}" if gw.domain else ""),
|
||||
)
|
||||
@@ -131,9 +131,9 @@ def _check_configuration(config) -> list[Check]:
|
||||
checks.append(
|
||||
Check(
|
||||
FAIL,
|
||||
"repo: not set in castle.yaml",
|
||||
detail="source: repo:<name> cannot resolve castle's own programs",
|
||||
hint="add 'repo: <path-to-castle-checkout>' to ~/.castle/castle.yaml",
|
||||
"repo: not set in wildpc.yaml",
|
||||
detail="source: repo:<name> cannot resolve wildpc's own programs",
|
||||
hint="add 'repo: <path-to-wildpc-checkout>' to ~/.wildpc/wildpc.yaml",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -148,22 +148,22 @@ def _check_configuration(config) -> list[Check]:
|
||||
FAIL,
|
||||
"data dir missing or not writable",
|
||||
detail=str(ddir),
|
||||
hint=f"set data_dir: in ~/.castle/castle.yaml, or: "
|
||||
hint=f"set data_dir: in ~/.wildpc/wildpc.yaml, or: "
|
||||
f"sudo mkdir -p {ddir} && sudo chown $(id -un) {ddir}",
|
||||
)
|
||||
)
|
||||
|
||||
# Drift guard: castle.yaml is the single source of truth for the roots. An env var
|
||||
# Drift guard: wildpc.yaml is the single source of truth for the roots. An env var
|
||||
# override is per-process, so it's the one way the CLI and the api service can still
|
||||
# diverge (env set in your shell, absent in the service unit — the original bug).
|
||||
for var in ("CASTLE_DATA_DIR", "CASTLE_REPOS_DIR"):
|
||||
for var in ("WILDPC_DATA_DIR", "WILDPC_REPOS_DIR"):
|
||||
if var in os.environ:
|
||||
checks.append(
|
||||
Check(
|
||||
WARN,
|
||||
f"{var} overrides castle.yaml",
|
||||
f"{var} overrides wildpc.yaml",
|
||||
detail=f"{var}={os.environ[var]}",
|
||||
hint=f"set data_dir:/repos_dir: in castle.yaml and unset {var}, so "
|
||||
hint=f"set data_dir:/repos_dir: in wildpc.yaml and unset {var}, so "
|
||||
"every process (CLI and api) resolves the same roots",
|
||||
)
|
||||
)
|
||||
@@ -186,7 +186,7 @@ def _check_configuration(config) -> list[Check]:
|
||||
|
||||
|
||||
def _check_dashboard_built(config) -> Check:
|
||||
from castle_core.lifecycle import _static_built
|
||||
from wildpc_core.lifecycle import _static_built
|
||||
|
||||
if not config.deployments_named(_DASHBOARD):
|
||||
return Check(WARN, "dashboard not registered", detail="skipping build check")
|
||||
@@ -196,7 +196,7 @@ def _check_dashboard_built(config) -> Check:
|
||||
WARN,
|
||||
"dashboard not built",
|
||||
detail="gateway has no UI to serve at /",
|
||||
hint=f"castle program build {_DASHBOARD}",
|
||||
hint=f"wildpc program build {_DASHBOARD}",
|
||||
)
|
||||
|
||||
|
||||
@@ -212,8 +212,8 @@ def _deployment_port(config, name: str) -> int | None:
|
||||
|
||||
|
||||
def _check_runtime(config) -> list[Check]:
|
||||
from castle_core.config import SPECS_DIR
|
||||
from castle_core.lifecycle import is_active
|
||||
from wildpc_core.config import SPECS_DIR
|
||||
from wildpc_core.lifecycle import is_active
|
||||
|
||||
checks: list[Check] = []
|
||||
|
||||
@@ -228,7 +228,7 @@ def _check_runtime(config) -> list[Check]:
|
||||
WARN,
|
||||
"gateway active but not listening",
|
||||
detail=f":{gw_port} refused",
|
||||
hint="castle gateway reload; check 'castle service logs castle-gateway'",
|
||||
hint="wildpc gateway reload; check 'wildpc service logs wildpc-gateway'",
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -236,7 +236,7 @@ def _check_runtime(config) -> list[Check]:
|
||||
Check(
|
||||
FAIL,
|
||||
"gateway not running",
|
||||
hint="castle apply",
|
||||
hint="wildpc apply",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -247,16 +247,16 @@ def _check_runtime(config) -> list[Check]:
|
||||
checks.append(
|
||||
Check(
|
||||
WARN,
|
||||
"castle-api active but not listening",
|
||||
"wildpc-api active but not listening",
|
||||
detail=f":{api_port} refused",
|
||||
hint="castle service logs castle-api",
|
||||
hint="wildpc service logs wildpc-api",
|
||||
)
|
||||
)
|
||||
else:
|
||||
detail = f"listening on :{api_port}" if api_port else ""
|
||||
checks.append(Check(OK, "castle-api running", detail=detail))
|
||||
checks.append(Check(OK, "wildpc-api running", detail=detail))
|
||||
else:
|
||||
checks.append(Check(FAIL, "castle-api not running", hint="castle apply"))
|
||||
checks.append(Check(FAIL, "wildpc-api not running", hint="wildpc apply"))
|
||||
|
||||
# Generated artifacts.
|
||||
registry = SPECS_DIR / "registry.yaml"
|
||||
@@ -270,7 +270,7 @@ def _check_runtime(config) -> list[Check]:
|
||||
FAIL,
|
||||
"generated specs missing",
|
||||
detail=", ".join(missing),
|
||||
hint="castle apply",
|
||||
hint="wildpc apply",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -293,7 +293,7 @@ def _check_tls_exposure(config) -> list[Check]:
|
||||
Check(
|
||||
FAIL,
|
||||
"tls=acme but no domain set",
|
||||
hint="add 'domain: <your-zone>' under gateway: in castle.yaml",
|
||||
hint="add 'domain: <your-zone>' under gateway: in wildpc.yaml",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -327,7 +327,7 @@ def _check_tls_exposure(config) -> list[Check]:
|
||||
token_name = {"cloudflare": "CLOUDFLARE_API_TOKEN"}.get(
|
||||
provider, f"{provider.upper()}_API_TOKEN"
|
||||
)
|
||||
from castle_core.config import read_secret
|
||||
from wildpc_core.config import read_secret
|
||||
|
||||
if read_secret(token_name):
|
||||
checks.append(Check(OK, "provider token present", detail=token_name))
|
||||
@@ -348,7 +348,7 @@ def _check_tls_exposure(config) -> list[Check]:
|
||||
public_specs = [(n, d) for _k, n, d in config.all_deployments() if getattr(d, "public", False)]
|
||||
public = [n for n, _d in public_specs]
|
||||
if public:
|
||||
from castle_core.lifecycle import is_active
|
||||
from wildpc_core.lifecycle import is_active
|
||||
|
||||
# A public deployment gets its public name from its own `public_host`
|
||||
# override or the node-wide `public_domain`; the default domain is only
|
||||
@@ -370,13 +370,13 @@ def _check_tls_exposure(config) -> list[Check]:
|
||||
hint="see docs/tunnel-setup.md",
|
||||
)
|
||||
)
|
||||
if not is_active("castle-tunnel", "service", config):
|
||||
if not is_active("wildpc-tunnel", "service", config):
|
||||
checks.append(
|
||||
Check(
|
||||
WARN,
|
||||
"castle-tunnel not running",
|
||||
"wildpc-tunnel not running",
|
||||
detail="public routes are down",
|
||||
hint="enable castle-tunnel in its deployment, then: castle apply",
|
||||
hint="enable wildpc-tunnel in its deployment, then: wildpc apply",
|
||||
)
|
||||
)
|
||||
checks.append(_check_public_dns(config))
|
||||
@@ -387,18 +387,18 @@ def _check_tls_exposure(config) -> list[Check]:
|
||||
|
||||
|
||||
def _check_public_dns(config) -> Check:
|
||||
"""Whether Castle can manage the public CNAMEs itself.
|
||||
"""Whether Wild PC can manage the public CNAMEs itself.
|
||||
|
||||
Read-only: confirms the token exists and can reach the public zone + its DNS
|
||||
records. The required permission is a single DNS:Edit (Cloudflare's 'Edit zone
|
||||
DNS' template), which also grants the zone lookup — so a correctly-scoped token
|
||||
passes this probe. Write itself isn't exercised (that would mutate); a
|
||||
DNS:Read-only token would false-pass here but `castle apply` then surfaces a
|
||||
DNS:Read-only token would false-pass here but `wildpc apply` then surfaces a
|
||||
403 with the fix. Absent token → WARN (CNAMEs stay manual), not a failure.
|
||||
"""
|
||||
import urllib.error
|
||||
|
||||
from castle_core.generators.dns import PUBLIC_DNS_TOKEN, _api, public_dns_token
|
||||
from wildpc_core.generators.dns import PUBLIC_DNS_TOKEN, _api, public_dns_token
|
||||
|
||||
gw = config.gateway
|
||||
token = public_dns_token()
|
||||
@@ -410,7 +410,7 @@ def _check_public_dns(config) -> Check:
|
||||
hint=(
|
||||
f"add a Cloudflare token with DNS:Edit on "
|
||||
f"{gw.public_domain or 'the public zone(s)'} "
|
||||
f"('Edit zone DNS' template) → ~/.castle/secrets/{PUBLIC_DNS_TOKEN} "
|
||||
f"('Edit zone DNS' template) → ~/.wildpc/secrets/{PUBLIC_DNS_TOKEN} "
|
||||
"(else route each host by hand)"
|
||||
),
|
||||
)
|
||||
@@ -463,7 +463,7 @@ def _check_privileged_ports() -> Check:
|
||||
"cannot bind :80/:443",
|
||||
detail=f"unprivileged floor is {val}",
|
||||
hint="echo 'net.ipv4.ip_unprivileged_port_start=80' | "
|
||||
"sudo tee /etc/sysctl.d/50-castle-gateway.conf && sudo sysctl --system",
|
||||
"sudo tee /etc/sysctl.d/50-wildpc-gateway.conf && sudo sysctl --system",
|
||||
)
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ def _check_stacks(config: object) -> list[Check]:
|
||||
tool for an enabled deployment is a FAIL (its service can't build/run); missing
|
||||
for a not-yet-enabled program is a WARN. Unused stacks are skipped — no nagging
|
||||
about pnpm when there are no frontends."""
|
||||
from castle_core.stack_status import all_stack_status
|
||||
from wildpc_core.stack_status import all_stack_status
|
||||
|
||||
checks: list[Check] = []
|
||||
for st in all_stack_status(config, with_version=False):
|
||||
@@ -504,9 +504,9 @@ def _check_stacks(config: object) -> list[Check]:
|
||||
|
||||
|
||||
def run_doctor(args: argparse.Namespace) -> int:
|
||||
from castle_core.config import load_config
|
||||
from wildpc_core.config import load_config
|
||||
|
||||
print("\n\033[1mCastle Doctor\033[0m")
|
||||
print("\n\033[1mWild PC Doctor\033[0m")
|
||||
|
||||
try:
|
||||
config = load_config()
|
||||
@@ -515,9 +515,9 @@ def run_doctor(args: argparse.Namespace) -> int:
|
||||
_print(
|
||||
Check(
|
||||
FAIL,
|
||||
"castle.yaml failed to load",
|
||||
"wildpc.yaml failed to load",
|
||||
detail=str(exc),
|
||||
hint="check ~/.castle/castle.yaml — re-run ./install.sh to reseed",
|
||||
hint="check ~/.wildpc/wildpc.yaml — re-run ./install.sh to reseed",
|
||||
)
|
||||
)
|
||||
print()
|
||||
@@ -544,7 +544,7 @@ def run_doctor(args: argparse.Namespace) -> int:
|
||||
print(f"\033[31m{fails} problem(s)\033[0m" + (f", {warns} warning(s)" if warns else ""))
|
||||
return 1
|
||||
if warns:
|
||||
print(f"\033[33m{warns} warning(s)\033[0m — Castle is up; address when convenient")
|
||||
print(f"\033[33m{warns} warning(s)\033[0m — Wild PC is up; address when convenient")
|
||||
return 0
|
||||
print("\033[32mAll checks passed.\033[0m")
|
||||
return 0
|
||||
@@ -1,8 +1,8 @@
|
||||
"""castle gateway — inspect the Caddy reverse proxy gateway.
|
||||
"""wildpc gateway — inspect the Caddy reverse proxy gateway.
|
||||
|
||||
The gateway is itself a deployment (`castle-gateway`): start/stop/reload it the
|
||||
same way as anything else — `castle apply` (render routes + reload), `castle
|
||||
restart castle-gateway` (bounce), or `enabled: false` + apply (stop). This command
|
||||
The gateway is itself a deployment (`wildpc-gateway`): start/stop/reload it the
|
||||
same way as anything else — `wildpc apply` (render routes + reload), `wildpc
|
||||
restart wildpc-gateway` (bounce), or `enabled: false` + apply (stop). This command
|
||||
is the read-only inspection lens: is it up, and what's the route table.
|
||||
"""
|
||||
|
||||
@@ -11,9 +11,9 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
from castle_core.registry import REGISTRY_PATH, load_registry
|
||||
from wildpc_core.registry import REGISTRY_PATH, load_registry
|
||||
|
||||
GATEWAY_UNIT = "castle-castle-gateway.service"
|
||||
GATEWAY_UNIT = "wildpc-wildpc-gateway.service"
|
||||
|
||||
|
||||
def run_gateway(args: argparse.Namespace) -> int:
|
||||
@@ -32,10 +32,10 @@ def _gateway_status() -> int:
|
||||
print(f"Gateway: {'running' if status == 'active' else status}")
|
||||
|
||||
if not REGISTRY_PATH.exists():
|
||||
print(" (no registry — run 'castle apply')")
|
||||
print(" (no registry — run 'wildpc apply')")
|
||||
return 0
|
||||
|
||||
from castle_core.generators.caddyfile import compute_routes
|
||||
from wildpc_core.generators.caddyfile import compute_routes
|
||||
|
||||
routes = compute_routes(load_registry())
|
||||
if not routes:
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle graph — the relationship model: repos, `requires` edges, derived status.
|
||||
"""wildpc graph — the relationship model: repos, `requires` edges, derived status.
|
||||
|
||||
A read-only diagnostic (see docs/relationships.md). Nothing here is stored — repos
|
||||
come from git, predicates (functional/fresh/deployed) are computed on the fly.
|
||||
@@ -10,14 +10,14 @@ import argparse
|
||||
import dataclasses
|
||||
import json
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
BOLD, DIM, RESET = "\033[1m", "\033[2m", "\033[0m"
|
||||
GREEN, RED, YELLOW, CYAN = "\033[32m", "\033[31m", "\033[33m", "\033[36m"
|
||||
|
||||
|
||||
def run_graph(args: argparse.Namespace) -> int:
|
||||
from castle_core.relations import build_model
|
||||
from wildpc_core.relations import build_model
|
||||
|
||||
config = load_config()
|
||||
model = build_model(config, check=True, freshness=True)
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle info - show detailed program information."""
|
||||
"""wildpc info - show detailed program information."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -6,7 +6,7 @@ import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
# Terminal colors
|
||||
BOLD = "\033[1m"
|
||||
@@ -20,7 +20,7 @@ RED = "\033[91m"
|
||||
def _load_deployed_program(name: str) -> object | None:
|
||||
"""Try to load a specific deployed program from registry."""
|
||||
try:
|
||||
from castle_core.registry import load_registry
|
||||
from wildpc_core.registry import load_registry
|
||||
|
||||
registry = load_registry()
|
||||
return next(iter(registry.named(name)), None)
|
||||
@@ -41,7 +41,7 @@ def run_info(args: argparse.Namespace) -> int:
|
||||
|
||||
if not program and not service and not job:
|
||||
where = f" {resource}" if resource else ""
|
||||
print(f"Error: no{where} '{name}' in castle.yaml")
|
||||
print(f"Error: no{where} '{name}' in wildpc.yaml")
|
||||
return 1
|
||||
|
||||
deployed = _load_deployed_program(name)
|
||||
@@ -153,7 +153,7 @@ def run_info(args: argparse.Namespace) -> int:
|
||||
if deployed.secret_env_keys:
|
||||
print(f" {BOLD}secrets{RESET}: {', '.join(deployed.secret_env_keys)}")
|
||||
else:
|
||||
print(f"\n {DIM}not applied (run 'castle apply'){RESET}")
|
||||
print(f"\n {DIM}not applied (run 'wildpc apply'){RESET}")
|
||||
|
||||
# Show CLAUDE.md if it exists
|
||||
source_dir = None
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle list - the program catalog plus every deployment view (services, jobs,
|
||||
"""wildpc list - the program catalog plus every deployment view (services, jobs,
|
||||
tools, static)."""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -7,7 +7,7 @@ import argparse
|
||||
import json
|
||||
import logging
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -75,7 +75,7 @@ def run_list(args: argparse.Namespace) -> int:
|
||||
and the **Services**/**Jobs** deployment views. `--kind` filters the catalog
|
||||
by a program's derived kind (service/job/tool/static/reference).
|
||||
"""
|
||||
from castle_core.lifecycle import is_active
|
||||
from wildpc_core.lifecycle import is_active
|
||||
|
||||
config = load_config()
|
||||
|
||||
@@ -199,7 +199,7 @@ def _list_json(
|
||||
filter_stack: str | None,
|
||||
) -> int:
|
||||
"""Output JSON: the program catalog (kind-filterable) plus deployments."""
|
||||
from castle_core.lifecycle import is_active
|
||||
from wildpc_core.lifecycle import is_active
|
||||
|
||||
output = []
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"""castle logs - view component logs."""
|
||||
"""wildpc logs - view component logs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
from castle_cli.commands.service import UNIT_PREFIX
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.commands.service import UNIT_PREFIX
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
def run_logs(args: argparse.Namespace) -> int:
|
||||
@@ -51,7 +51,7 @@ def _compose_logs(name: str, svc: object, args: argparse.Namespace) -> int:
|
||||
import shutil
|
||||
|
||||
runtime = shutil.which("docker") or shutil.which("podman") or "docker"
|
||||
project = getattr(svc.run, "project_name", None) or f"castle-{name}" # type: ignore[attr-defined]
|
||||
project = getattr(svc.run, "project_name", None) or f"wildpc-{name}" # type: ignore[attr-defined]
|
||||
cmd = [runtime, "compose", "-p", project, "logs"]
|
||||
|
||||
lines = getattr(args, "lines", 50)
|
||||
@@ -70,7 +70,7 @@ def _container_logs(name: str, args: argparse.Namespace) -> int:
|
||||
import shutil
|
||||
|
||||
runtime = shutil.which("podman") or shutil.which("docker") or "podman"
|
||||
container_name = f"castle-{name}"
|
||||
container_name = f"wildpc-{name}"
|
||||
cmd = [runtime, "logs"]
|
||||
|
||||
lines = getattr(args, "lines", 50)
|
||||
@@ -1,6 +1,6 @@
|
||||
"""castle mesh — inspect the NATS mesh and manage shared config.
|
||||
"""wildpc mesh — inspect the NATS mesh and manage shared config.
|
||||
|
||||
The mesh lives in the running castle-api (it holds the live peer state), so this
|
||||
The mesh lives in the running wildpc-api (it holds the live peer state), so this
|
||||
command talks to the local API over HTTP rather than reading files.
|
||||
"""
|
||||
|
||||
@@ -15,10 +15,10 @@ import urllib.request
|
||||
def _api_base() -> str:
|
||||
port = None
|
||||
try:
|
||||
from castle_core.config import load_config
|
||||
from wildpc_core.config import load_config
|
||||
|
||||
config = load_config()
|
||||
dep = next((d for _k, d in config.deployments_named("castle-api")), None)
|
||||
dep = next((d for _k, d in config.deployments_named("wildpc-api")), None)
|
||||
internal = getattr(getattr(getattr(dep, "expose", None), "http", None), "internal", None)
|
||||
port = getattr(internal, "port", None)
|
||||
except Exception:
|
||||
@@ -56,7 +56,7 @@ def run_mesh(args: argparse.Namespace) -> int:
|
||||
print(f"error: HTTP {e.code} — {detail}")
|
||||
return 1
|
||||
except urllib.error.URLError as e:
|
||||
print(f"castle-api not reachable ({e.reason}). Is it running + mesh enabled?")
|
||||
print(f"wildpc-api not reachable ({e.reason}). Is it running + mesh enabled?")
|
||||
return 1
|
||||
print(f"unknown mesh command: {sub}")
|
||||
return 2
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle run - run a program or service in the foreground.
|
||||
"""wildpc run - run a program or service in the foreground.
|
||||
|
||||
Unified: if the target is a program with a declared `run` command, run that in
|
||||
the foreground (dev-run). Otherwise fall back to the deployed-service run from
|
||||
@@ -12,10 +12,10 @@ import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from castle_core.generators.systemd import secret_env_path
|
||||
from castle_core.registry import REGISTRY_PATH, load_registry
|
||||
from wildpc_core.generators.systemd import secret_env_path
|
||||
from wildpc_core.registry import REGISTRY_PATH, load_registry
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
def _load_secret_env(name: str) -> dict[str, str]:
|
||||
@@ -82,13 +82,13 @@ def run_run(args: argparse.Namespace) -> int:
|
||||
|
||||
# Service: deployed command from the registry.
|
||||
if not REGISTRY_PATH.exists():
|
||||
print("Error: no registry found. Run 'castle apply' first.")
|
||||
print("Error: no registry found. Run 'wildpc apply' first.")
|
||||
return 1
|
||||
|
||||
registry = load_registry()
|
||||
matches = registry.named(name)
|
||||
if not matches:
|
||||
print(f"Error: '{name}' is not a deployed service. Run 'castle apply' first.")
|
||||
print(f"Error: '{name}' is not a deployed service. Run 'wildpc apply' first.")
|
||||
return 1
|
||||
|
||||
# A name may span kinds; run the one that has a run command (service/job).
|
||||
@@ -1,14 +1,14 @@
|
||||
"""castle secret — read/write secrets in the *active* backend.
|
||||
"""wildpc secret — read/write secrets in the *active* backend.
|
||||
|
||||
The active backend (file or openbao) is selected by the ``secrets:`` block of
|
||||
castle.yaml (env overrides). Writing a secret by hand means knowing that choice
|
||||
wildpc.yaml (env overrides). Writing a secret by hand means knowing that choice
|
||||
and the backend's storage layout — get it wrong and the value lands somewhere the
|
||||
resolver never reads, so ``${secret:NAME}`` silently degrades to a
|
||||
``<MISSING_SECRET:NAME>`` placeholder that a service then uses as if it were the
|
||||
real credential (exactly how immich's DB password went to a file on an OpenBao
|
||||
fleet). This command routes every read/write through
|
||||
:func:`castle_core.config.active_secret_backend`, so there's no wrong store to
|
||||
pick. ``castle apply`` refuses to converge a deployment whose secrets don't
|
||||
:func:`wildpc_core.config.active_secret_backend`, so there's no wrong store to
|
||||
pick. ``wildpc apply`` refuses to converge a deployment whose secrets don't
|
||||
resolve here.
|
||||
"""
|
||||
|
||||
@@ -18,13 +18,13 @@ import argparse
|
||||
import getpass
|
||||
import sys
|
||||
|
||||
from castle_core.config import active_backend_name, active_secret_backend
|
||||
from wildpc_core.config import active_backend_name, active_secret_backend
|
||||
|
||||
|
||||
def run_secret(args: argparse.Namespace) -> int:
|
||||
sub = getattr(args, "secret_command", None)
|
||||
if not sub:
|
||||
print("Usage: castle secret {list|set|get|rm}")
|
||||
print("Usage: wildpc secret {list|set|get|rm}")
|
||||
return 1
|
||||
if sub == "list":
|
||||
return _list()
|
||||
@@ -1,23 +1,23 @@
|
||||
"""castle service / castle job — manage systemd service & timer units."""
|
||||
"""wildpc service / wildpc job — manage systemd service & timer units."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
from castle_core.generators.systemd import (
|
||||
from wildpc_core.generators.systemd import (
|
||||
SYSTEMD_USER_DIR,
|
||||
timer_name,
|
||||
unit_name,
|
||||
)
|
||||
|
||||
from castle_cli.config import (
|
||||
CastleConfig,
|
||||
from wildpc_cli.config import (
|
||||
WildpcConfig,
|
||||
load_config,
|
||||
)
|
||||
|
||||
# Re-export for use by other commands
|
||||
UNIT_PREFIX = "castle-"
|
||||
UNIT_PREFIX = "wildpc-"
|
||||
|
||||
|
||||
def _install_unit(uname: str, content: str) -> None:
|
||||
@@ -40,25 +40,25 @@ _PAST = {"start": "started", "stop": "stopped", "restart": "restarted"}
|
||||
|
||||
|
||||
def run_service_cmd(args: argparse.Namespace) -> int:
|
||||
"""`castle service restart <name>` — the imperative bounce (only verb left).
|
||||
"""`wildpc service restart <name>` — the imperative bounce (only verb left).
|
||||
|
||||
Lifecycle (deploy/enable/disable/start/stop) is now convergence: `castle apply`.
|
||||
Lifecycle (deploy/enable/disable/start/stop) is now convergence: `wildpc apply`.
|
||||
"""
|
||||
config = load_config()
|
||||
return _unit_action(config, args.name, "restart", "service")
|
||||
|
||||
|
||||
def run_job_cmd(args: argparse.Namespace) -> int:
|
||||
"""`castle job restart <name>` — bounce the job's timer."""
|
||||
"""`wildpc job restart <name>` — bounce the job's timer."""
|
||||
config = load_config()
|
||||
return _unit_action(config, args.name, "restart", "job")
|
||||
|
||||
|
||||
def run_restart(args: argparse.Namespace) -> int:
|
||||
"""Top-level `castle restart [name]` — bounce one deployment, or all of them.
|
||||
"""Top-level `wildpc restart [name]` — bounce one deployment, or all of them.
|
||||
|
||||
An imperative op: it re-actualizes current desired state, it does not change it
|
||||
(that's `castle apply`). A bare name bounces every kind sharing it.
|
||||
(that's `wildpc apply`). A bare name bounces every kind sharing it.
|
||||
"""
|
||||
config = load_config()
|
||||
name = getattr(args, "name", None)
|
||||
@@ -74,10 +74,10 @@ def run_restart(args: argparse.Namespace) -> int:
|
||||
return rc
|
||||
|
||||
|
||||
_GATEWAY_NAME = "castle-gateway"
|
||||
_GATEWAY_NAME = "wildpc-gateway"
|
||||
|
||||
|
||||
def _unit_action(config: CastleConfig, name: str, action: str, kind: str) -> int:
|
||||
def _unit_action(config: WildpcConfig, name: str, action: str, kind: str) -> int:
|
||||
"""start/stop/restart one deployment (name, kind), dispatched by its manager.
|
||||
|
||||
systemd (a process/timer) → `systemctl`; caddy (static) → reload the gateway;
|
||||
@@ -101,7 +101,7 @@ def _unit_action(config: CastleConfig, name: str, action: str, kind: str) -> int
|
||||
|
||||
|
||||
def _managed_lifecycle(
|
||||
config: CastleConfig, name: str, action: str, manager: str, kind: str
|
||||
config: WildpcConfig, name: str, action: str, manager: str, kind: str
|
||||
) -> int:
|
||||
"""Lifecycle for non-systemd managers (no unit to systemctl)."""
|
||||
if manager == "caddy":
|
||||
@@ -119,11 +119,11 @@ def _managed_lifecycle(
|
||||
return 0
|
||||
|
||||
|
||||
def _path_lifecycle(config: CastleConfig, name: str, action: str, kind: str) -> int:
|
||||
def _path_lifecycle(config: WildpcConfig, name: str, action: str, kind: str) -> int:
|
||||
"""A `path` (tool) deployment's lifecycle is install/uninstall on PATH."""
|
||||
import asyncio
|
||||
|
||||
from castle_core.lifecycle import activate, deactivate
|
||||
from wildpc_core.lifecycle import activate, deactivate
|
||||
|
||||
# stop → uninstall; start/restart → ensure installed (activate skips if on PATH).
|
||||
coro = deactivate if action == "stop" else activate
|
||||
@@ -132,7 +132,7 @@ def _path_lifecycle(config: CastleConfig, name: str, action: str, kind: str) ->
|
||||
return 0 if res.status == "ok" else 1
|
||||
|
||||
|
||||
def _services_restart(config: CastleConfig) -> int:
|
||||
def _services_restart(config: WildpcConfig) -> int:
|
||||
"""Restart every systemd-managed deployment (service or job) unit.
|
||||
|
||||
caddy/path/none deployments have no unit — they ride along with the gateway
|
||||
@@ -152,7 +152,7 @@ def _services_restart(config: CastleConfig) -> int:
|
||||
|
||||
def run_status(args: argparse.Namespace) -> int:
|
||||
"""Unified status across the platform: services + jobs + programs."""
|
||||
from castle_core.lifecycle import is_active
|
||||
from wildpc_core.lifecycle import is_active
|
||||
|
||||
config = load_config()
|
||||
|
||||
@@ -180,11 +180,11 @@ def run_status(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _service_status(config: CastleConfig) -> int:
|
||||
def _service_status(config: WildpcConfig) -> int:
|
||||
"""Show status of all services and jobs, dispatched by manager."""
|
||||
from castle_core.lifecycle import is_active
|
||||
from wildpc_core.lifecycle import is_active
|
||||
|
||||
print("\nCastle Services")
|
||||
print("\nWild PC Services")
|
||||
print("=" * 50)
|
||||
|
||||
for name, svc in config.services.items():
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle stack — the stacks lens (toolchains a program's stack needs).
|
||||
"""wildpc stack — the stacks lens (toolchains a program's stack needs).
|
||||
|
||||
A *stack* (python-fastapi, react-vite, hugo, …) is creation-time guidance that
|
||||
also carries the **host toolchains** its programs need to build and run (`uv`,
|
||||
@@ -14,10 +14,10 @@ import json
|
||||
from dataclasses import asdict
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from castle_core.stack_status import StackStatus
|
||||
from wildpc_core.stack_status import StackStatus
|
||||
|
||||
BOLD = "\033[1m"
|
||||
DIM = "\033[2m"
|
||||
@@ -37,7 +37,7 @@ def _record(st: StackStatus) -> dict:
|
||||
|
||||
def run_stack_list(args: argparse.Namespace) -> int:
|
||||
"""List stacks with their toolchain health, program count, and dev verbs."""
|
||||
from castle_core.stack_status import all_stack_status
|
||||
from wildpc_core.stack_status import all_stack_status
|
||||
|
||||
config = load_config()
|
||||
# Skip per-tool version probes for the list (a subprocess per tool) — keep it snappy.
|
||||
@@ -75,13 +75,13 @@ def run_stack_list(args: argparse.Namespace) -> int:
|
||||
|
||||
def run_stack_info(args: argparse.Namespace) -> int:
|
||||
"""Show one stack: each tool's presence + version + fix, and who uses it."""
|
||||
from castle_core.stack_status import stack_status
|
||||
from wildpc_core.stack_status import stack_status
|
||||
|
||||
config = load_config()
|
||||
name = args.name
|
||||
st = stack_status(config, name)
|
||||
if st is None:
|
||||
from castle_core.stacks import available_stacks
|
||||
from wildpc_core.stacks import available_stacks
|
||||
|
||||
print(f"Error: no stack '{name}'. Known: {', '.join(available_stacks())}")
|
||||
return 1
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle tls — castle-managed TLS certs for raw-TCP services.
|
||||
"""wildpc tls — wildpc-managed TLS certs for raw-TCP services.
|
||||
|
||||
Each TLS-material TCP service (postgres, redis, …) gets the gateway's ACME
|
||||
wildcard cert cut onto it so it presents a *trusted* cert on its raw port.
|
||||
@@ -13,9 +13,9 @@ import argparse
|
||||
import hashlib
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from castle_core.tls import _tls_of, reconcile_tls, tls_dir_for, wildcard_cert
|
||||
from wildpc_core.tls import _tls_of, reconcile_tls, tls_dir_for, wildcard_cert
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
def run_tls(args: argparse.Namespace) -> int:
|
||||
@@ -67,7 +67,7 @@ def _tls_status() -> int:
|
||||
continue
|
||||
pem = have.read_bytes()
|
||||
fp = _fingerprint(pem)
|
||||
drift = "" if src_fp and fp == src_fp else " ⚠ stale (run: castle tls reconcile)"
|
||||
drift = "" if src_fp and fp == src_fp else " ⚠ stale (run: wildpc tls reconcile)"
|
||||
rows.append((name, fp, _not_after(pem), drift))
|
||||
|
||||
if not rows:
|
||||
@@ -1,4 +1,4 @@
|
||||
"""castle tool — the tools lens (programs installed on PATH).
|
||||
"""wildpc tool — the tools lens (programs installed on PATH).
|
||||
|
||||
A *tool* is a program with a `path` (manager) deployment: a CLI on your PATH.
|
||||
This lens is what coding assistants use to discover what's available — so the
|
||||
@@ -15,11 +15,11 @@ import tomllib
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from castle_core.config import CastleConfig
|
||||
from castle_core.manifest import ProgramSpec
|
||||
from wildpc_core.config import WildpcConfig
|
||||
from wildpc_core.manifest import ProgramSpec
|
||||
|
||||
BOLD = "\033[1m"
|
||||
DIM = "\033[2m"
|
||||
@@ -29,11 +29,11 @@ GREY = "\033[90m"
|
||||
CYAN = "\033[96m"
|
||||
|
||||
|
||||
def _is_tool(config: CastleConfig, name: str) -> bool:
|
||||
def _is_tool(config: WildpcConfig, name: str) -> bool:
|
||||
return any(kind == "tool" for _, kind in config.deployments_of(name))
|
||||
|
||||
|
||||
def _tool_programs(config: CastleConfig) -> dict:
|
||||
def _tool_programs(config: WildpcConfig) -> dict:
|
||||
"""Programs with a tool (path) deployment, name-sorted."""
|
||||
return {name: comp for name, comp in sorted(config.programs.items()) if _is_tool(config, name)}
|
||||
|
||||
@@ -60,12 +60,12 @@ def _executables(comp: ProgramSpec) -> list[str]:
|
||||
|
||||
|
||||
def _tool_record(
|
||||
config: CastleConfig, name: str, comp: ProgramSpec, installed: bool, fmt: str = "openai"
|
||||
config: WildpcConfig, name: str, comp: ProgramSpec, installed: bool, fmt: str = "openai"
|
||||
) -> dict:
|
||||
# The tool-call schema (for handing this CLI to an agent) is stored neutral on
|
||||
# the path deployment; render it into the requested envelope for the --json
|
||||
# context payload. Feed default is openai (litellm-native).
|
||||
from castle_core.tool_schema import render_tool_schema
|
||||
from wildpc_core.tool_schema import render_tool_schema
|
||||
|
||||
dep = config.deployment("tool", name)
|
||||
core = getattr(dep, "tool_schema", None)
|
||||
@@ -83,7 +83,7 @@ def _tool_record(
|
||||
|
||||
def run_tool_list(args: argparse.Namespace) -> int:
|
||||
"""List tools (programs on PATH) with their executable + description."""
|
||||
from castle_core.lifecycle import tool_installed
|
||||
from wildpc_core.lifecycle import tool_installed
|
||||
|
||||
config = load_config()
|
||||
tools = _tool_programs(config)
|
||||
@@ -119,7 +119,7 @@ def run_tool_list(args: argparse.Namespace) -> int:
|
||||
|
||||
def run_tool_info(args: argparse.Namespace) -> int:
|
||||
"""Show one tool's details — executable, description, install state, source."""
|
||||
from castle_core.lifecycle import tool_installed
|
||||
from wildpc_core.lifecycle import tool_installed
|
||||
|
||||
config = load_config()
|
||||
name = args.name
|
||||
@@ -149,7 +149,7 @@ def run_tool_info(args: argparse.Namespace) -> int:
|
||||
if comp.system_dependencies:
|
||||
print(f" {BOLD}requires{RESET}: {', '.join(comp.system_dependencies)}")
|
||||
if not installed:
|
||||
print(f"\n {DIM}enable it in its deployment, then: castle apply{RESET}")
|
||||
print(f"\n {DIM}enable it in its deployment, then: wildpc apply{RESET}")
|
||||
else:
|
||||
print(f"\n {DIM}run `{exes[0]} --help` for arguments{RESET}")
|
||||
print()
|
||||
@@ -1,24 +1,24 @@
|
||||
"""Re-export from castle-core for backward compatibility."""
|
||||
"""Re-export from wildpc-core for backward compatibility."""
|
||||
|
||||
from castle_core.config import * # noqa: F401, F403
|
||||
from castle_core.config import ( # noqa: F401 — explicit re-exports for type checkers
|
||||
from wildpc_core.config import * # noqa: F401, F403
|
||||
from wildpc_core.config import ( # noqa: F401 — explicit re-exports for type checkers
|
||||
ARTIFACTS_DIR,
|
||||
CASTLE_HOME,
|
||||
CODE_DIR,
|
||||
CONTENT_DIR,
|
||||
GENERATED_DIR,
|
||||
SECRETS_DIR,
|
||||
SPECS_DIR,
|
||||
STATIC_DIR,
|
||||
CastleConfig,
|
||||
WILDPC_HOME,
|
||||
GatewayConfig,
|
||||
WildpcConfig,
|
||||
ensure_dirs,
|
||||
find_castle_root,
|
||||
find_wildpc_root,
|
||||
load_config,
|
||||
resolve_env_vars,
|
||||
save_config,
|
||||
)
|
||||
from castle_core.registry import ( # noqa: F401
|
||||
from wildpc_core.registry import ( # noqa: F401
|
||||
REGISTRY_PATH,
|
||||
Deployment,
|
||||
NodeConfig,
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Castle CLI entry point — resource-first command surface.
|
||||
"""Wild PC CLI entry point — resource-first command surface.
|
||||
|
||||
Operations live under the resource they act on. `program` is the catalog;
|
||||
`service`, `job`, and `tool` are deployment lenses (systemd services, scheduled
|
||||
@@ -13,9 +13,9 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from castle_core.stacks import available_stacks
|
||||
from wildpc_core.stacks import available_stacks
|
||||
|
||||
from castle_cli import __version__
|
||||
from wildpc_cli import __version__
|
||||
|
||||
DEV_VERBS = ["build", "test", "lint", "format", "type-check", "check"]
|
||||
|
||||
@@ -56,7 +56,7 @@ def _build_program_group(subparsers: argparse._SubParsersAction) -> None:
|
||||
p = sub.add_parser("clone", help="Clone source for programs with repo:")
|
||||
_add_name(p, "Program to clone (default: all with repo:)", optional=True)
|
||||
|
||||
p = sub.add_parser("delete", help="Remove a program from castle.yaml")
|
||||
p = sub.add_parser("delete", help="Remove a program from wildpc.yaml")
|
||||
_add_name(p, "Program name")
|
||||
p.add_argument("--source", action="store_true", help="Also delete the source directory")
|
||||
p.add_argument(
|
||||
@@ -109,7 +109,7 @@ def _build_stack_group(subparsers: argparse._SubParsersAction) -> None:
|
||||
|
||||
|
||||
def _add_service_create(sub: argparse._SubParsersAction, kind: str) -> None:
|
||||
p = sub.add_parser("create", help=f"Create a {kind} in castle.yaml")
|
||||
p = sub.add_parser("create", help=f"Create a {kind} in wildpc.yaml")
|
||||
_add_name(p, f"{kind.capitalize()} name")
|
||||
p.add_argument("--program", help="Program this deployment runs (convenience ref)")
|
||||
p.add_argument("--description", default="", help="Description")
|
||||
@@ -148,14 +148,14 @@ def _build_deployment_group(subparsers: argparse._SubParsersAction, kind: str) -
|
||||
|
||||
_add_service_create(sub, kind)
|
||||
|
||||
p = sub.add_parser("delete", help=f"Remove a {kind} from castle.yaml")
|
||||
p = sub.add_parser("delete", help=f"Remove a {kind} from wildpc.yaml")
|
||||
_add_name(p, f"{kind.capitalize()} name")
|
||||
p.add_argument("-y", "--yes", action="store_true", help="Skip confirmation")
|
||||
p.add_argument("--source", action="store_true", help=argparse.SUPPRESS)
|
||||
p.add_argument("--purge-data", action="store_true", help=argparse.SUPPRESS)
|
||||
|
||||
cap = f"{kind.capitalize()} name"
|
||||
# Lifecycle is convergence: `castle apply [name]`. `restart` stays as the one
|
||||
# Lifecycle is convergence: `wildpc apply [name]`. `restart` stays as the one
|
||||
# imperative bounce that doesn't change desired state.
|
||||
_add_name(sub.add_parser("restart", help=f"Restart the {kind} (imperative bounce)"), cap)
|
||||
|
||||
@@ -167,13 +167,13 @@ def _build_deployment_group(subparsers: argparse._SubParsersAction, kind: str) -
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="castle",
|
||||
prog="wildpc",
|
||||
description=(
|
||||
"Castle platform CLI — programs, deployments "
|
||||
"Wild PC platform CLI — programs, deployments "
|
||||
"(services, jobs, tools), and infrastructure"
|
||||
),
|
||||
)
|
||||
parser.add_argument("--version", action="version", version=f"castle {__version__}")
|
||||
parser.add_argument("--version", action="version", version=f"wildpc {__version__}")
|
||||
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
||||
|
||||
_build_program_group(subparsers)
|
||||
@@ -183,7 +183,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
_build_stack_group(subparsers)
|
||||
|
||||
# Gateway (inspection). The gateway is a deployment — start/stop/reload it via
|
||||
# `castle apply` / `castle restart castle-gateway`; this lens just shows routes.
|
||||
# `wildpc apply` / `wildpc restart wildpc-gateway`; this lens just shows routes.
|
||||
gw = subparsers.add_parser("gateway", help="Show the gateway's status + route table")
|
||||
gw_sub = gw.add_subparsers(dest="gateway_command")
|
||||
gw_sub.add_parser("status", help="Show gateway status + routes (the default)")
|
||||
@@ -204,7 +204,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
# TLS material for raw-TCP services (cert cut from the gateway wildcard).
|
||||
tls = subparsers.add_parser(
|
||||
"tls", help="Manage castle-materialized TLS certs for raw-TCP services"
|
||||
"tls", help="Manage wildpc-materialized TLS certs for raw-TCP services"
|
||||
)
|
||||
tls_sub = tls.add_subparsers(dest="tls_command")
|
||||
tls_sub.add_parser(
|
||||
@@ -265,38 +265,38 @@ def _dispatch_program(args: argparse.Namespace) -> int:
|
||||
sub = args.program_command
|
||||
if not sub:
|
||||
verbs = "list|info|create|add|clone|delete|run|" + "|".join(DEV_VERBS)
|
||||
print(f"Usage: castle program {{{verbs}}}")
|
||||
print(f"Usage: wildpc program {{{verbs}}}")
|
||||
return 1
|
||||
if sub == "list":
|
||||
from castle_cli.commands.list_cmd import run_list
|
||||
from wildpc_cli.commands.list_cmd import run_list
|
||||
|
||||
return run_list(args)
|
||||
if sub == "info":
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
return run_info(args)
|
||||
if sub == "create":
|
||||
from castle_cli.commands.create import run_create
|
||||
from wildpc_cli.commands.create import run_create
|
||||
|
||||
return run_create(args)
|
||||
if sub == "add":
|
||||
from castle_cli.commands.add import run_add
|
||||
from wildpc_cli.commands.add import run_add
|
||||
|
||||
return run_add(args)
|
||||
if sub == "clone":
|
||||
from castle_cli.commands.clone import run_clone
|
||||
from wildpc_cli.commands.clone import run_clone
|
||||
|
||||
return run_clone(args)
|
||||
if sub == "delete":
|
||||
from castle_cli.commands.delete import run_delete
|
||||
from wildpc_cli.commands.delete import run_delete
|
||||
|
||||
return run_delete(args)
|
||||
if sub == "run":
|
||||
from castle_cli.commands.run_cmd import run_run
|
||||
from wildpc_cli.commands.run_cmd import run_run
|
||||
|
||||
return run_run(args)
|
||||
if sub in DEV_VERBS:
|
||||
from castle_cli.commands.dev import run_verb
|
||||
from wildpc_cli.commands.dev import run_verb
|
||||
|
||||
return run_verb(args, sub)
|
||||
return 1
|
||||
@@ -305,14 +305,14 @@ def _dispatch_program(args: argparse.Namespace) -> int:
|
||||
def _dispatch_tool(args: argparse.Namespace) -> int:
|
||||
sub = args.tool_command
|
||||
if not sub:
|
||||
print("Usage: castle tool {list|info} (install/uninstall → edit config + castle apply)")
|
||||
print("Usage: wildpc tool {list|info} (install/uninstall → edit config + wildpc apply)")
|
||||
return 1
|
||||
if sub == "list":
|
||||
from castle_cli.commands.tool import run_tool_list
|
||||
from wildpc_cli.commands.tool import run_tool_list
|
||||
|
||||
return run_tool_list(args)
|
||||
if sub == "info":
|
||||
from castle_cli.commands.tool import run_tool_info
|
||||
from wildpc_cli.commands.tool import run_tool_info
|
||||
|
||||
return run_tool_info(args)
|
||||
return 1
|
||||
@@ -321,14 +321,14 @@ def _dispatch_tool(args: argparse.Namespace) -> int:
|
||||
def _dispatch_stack(args: argparse.Namespace) -> int:
|
||||
sub = args.stack_command
|
||||
if not sub:
|
||||
print("Usage: castle stack {list|info}")
|
||||
print("Usage: wildpc stack {list|info}")
|
||||
return 1
|
||||
if sub == "list":
|
||||
from castle_cli.commands.stack import run_stack_list
|
||||
from wildpc_cli.commands.stack import run_stack_list
|
||||
|
||||
return run_stack_list(args)
|
||||
if sub == "info":
|
||||
from castle_cli.commands.stack import run_stack_info
|
||||
from wildpc_cli.commands.stack import run_stack_info
|
||||
|
||||
return run_stack_info(args)
|
||||
return 1
|
||||
@@ -337,31 +337,31 @@ def _dispatch_stack(args: argparse.Namespace) -> int:
|
||||
def _dispatch_deployment(args: argparse.Namespace, kind: str) -> int:
|
||||
sub = getattr(args, f"{kind}_command")
|
||||
if not sub:
|
||||
verbs = "list|info|create|delete|restart|logs (deploy/enable/... → castle apply)"
|
||||
print(f"Usage: castle {kind} {{{verbs}}}")
|
||||
verbs = "list|info|create|delete|restart|logs (deploy/enable/... → wildpc apply)"
|
||||
print(f"Usage: wildpc {kind} {{{verbs}}}")
|
||||
return 1
|
||||
if sub == "list":
|
||||
from castle_cli.commands.list_cmd import run_list
|
||||
from wildpc_cli.commands.list_cmd import run_list
|
||||
|
||||
return run_list(args)
|
||||
if sub == "info":
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
return run_info(args)
|
||||
if sub == "create":
|
||||
from castle_cli.commands.deploy_create import run_job_create, run_service_create
|
||||
from wildpc_cli.commands.deploy_create import run_job_create, run_service_create
|
||||
|
||||
return run_service_create(args) if kind == "service" else run_job_create(args)
|
||||
if sub == "delete":
|
||||
from castle_cli.commands.delete import run_delete
|
||||
from wildpc_cli.commands.delete import run_delete
|
||||
|
||||
return run_delete(args)
|
||||
if sub == "restart":
|
||||
from castle_cli.commands.service import run_job_cmd, run_service_cmd
|
||||
from wildpc_cli.commands.service import run_job_cmd, run_service_cmd
|
||||
|
||||
return run_service_cmd(args) if kind == "service" else run_job_cmd(args)
|
||||
if sub == "logs":
|
||||
from castle_cli.commands.logs import run_logs
|
||||
from wildpc_cli.commands.logs import run_logs
|
||||
|
||||
return run_logs(args)
|
||||
return 1
|
||||
@@ -385,43 +385,43 @@ def main() -> int:
|
||||
if cmd == "stack":
|
||||
return _dispatch_stack(args)
|
||||
if cmd == "gateway":
|
||||
from castle_cli.commands.gateway import run_gateway
|
||||
from wildpc_cli.commands.gateway import run_gateway
|
||||
|
||||
return run_gateway(args)
|
||||
if cmd == "mesh":
|
||||
from castle_cli.commands.mesh import run_mesh
|
||||
from wildpc_cli.commands.mesh import run_mesh
|
||||
|
||||
return run_mesh(args)
|
||||
if cmd == "tls":
|
||||
from castle_cli.commands.tls import run_tls
|
||||
from wildpc_cli.commands.tls import run_tls
|
||||
|
||||
return run_tls(args)
|
||||
if cmd == "secret":
|
||||
from castle_cli.commands.secret import run_secret
|
||||
from wildpc_cli.commands.secret import run_secret
|
||||
|
||||
return run_secret(args)
|
||||
if cmd == "apply":
|
||||
from castle_cli.commands.apply import run_apply
|
||||
from wildpc_cli.commands.apply import run_apply
|
||||
|
||||
return run_apply(args)
|
||||
if cmd == "restart":
|
||||
from castle_cli.commands.service import run_restart
|
||||
from wildpc_cli.commands.service import run_restart
|
||||
|
||||
return run_restart(args)
|
||||
if cmd == "status":
|
||||
from castle_cli.commands.service import run_status
|
||||
from wildpc_cli.commands.service import run_status
|
||||
|
||||
return run_status(args)
|
||||
if cmd == "doctor":
|
||||
from castle_cli.commands.doctor import run_doctor
|
||||
from wildpc_cli.commands.doctor import run_doctor
|
||||
|
||||
return run_doctor(args)
|
||||
if cmd == "graph":
|
||||
from castle_cli.commands.graph import run_graph
|
||||
from wildpc_cli.commands.graph import run_graph
|
||||
|
||||
return run_graph(args)
|
||||
if cmd == "list":
|
||||
from castle_cli.commands.list_cmd import run_list
|
||||
from wildpc_cli.commands.list_cmd import run_list
|
||||
|
||||
return run_list(args)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Re-export from castle-core for backward compatibility."""
|
||||
"""Re-export from wildpc-core for backward compatibility."""
|
||||
|
||||
from castle_core.manifest import * # noqa: F401, F403
|
||||
from castle_core.manifest import ( # noqa: F401 — explicit re-exports for type checkers
|
||||
from wildpc_core.manifest import * # noqa: F401, F403
|
||||
from wildpc_core.manifest import ( # noqa: F401 — explicit re-exports for type checkers
|
||||
BuildSpec,
|
||||
CaddyDeployment,
|
||||
CommandsSpec,
|
||||
@@ -575,14 +575,14 @@ uv run ruff format . # Format
|
||||
def _scaffold_supabase(project_dir: Path, name: str, description: str) -> None:
|
||||
"""Scaffold a Patch-shaped app that targets the shared Supabase substrate.
|
||||
|
||||
Produces migrations/ (applied to the substrate by `castle program build`),
|
||||
Produces migrations/ (applied to the substrate by `wildpc program build`),
|
||||
functions/ (deno edge functions), public/ (static UI served in place at
|
||||
<name>.<gateway.domain> by the gateway), and supabase.app.yaml (auth policy + wiring).
|
||||
|
||||
The app owns its code and stays repo-durable; only its rows/blobs live on the
|
||||
shared substrate. Each app is isolated in its **own Postgres schema** (the app
|
||||
id) rather than sharing `public` — so `castle program build` creates+grants the
|
||||
schema and tracks migrations per-app, and `castle delete --purge-data` drops
|
||||
id) rather than sharing `public` — so `wildpc program build` creates+grants the
|
||||
schema and tracks migrations per-app, and `wildpc delete --purge-data` drops
|
||||
the whole schema cleanly. Rows are further protected by RLS.
|
||||
"""
|
||||
ident = name.replace("-", "_") # a safe SQL/JS identifier — and the app schema
|
||||
@@ -604,7 +604,7 @@ def _scaffold_supabase(project_dir: Path, name: str, description: str) -> None:
|
||||
"""# Patch-shaped app targeting the shared Supabase substrate.
|
||||
name: __NAME__
|
||||
description: __DESC__
|
||||
substrate: supabase # the shared castle service this app deploys against
|
||||
substrate: supabase # the shared wildpc service this app deploys against
|
||||
|
||||
# auth policy: public | private | shared
|
||||
# public — anyone with the URL (anon read/write, still RLS-gated)
|
||||
@@ -614,7 +614,7 @@ auth: public
|
||||
# shared: [alice, bob]
|
||||
|
||||
# This app is isolated in its own Postgres schema (created + exposed through
|
||||
# PostgREST by `castle program build`). The frontend selects it via
|
||||
# PostgREST by `wildpc program build`). The frontend selects it via
|
||||
# supabase-js `db: { schema }`.
|
||||
schema: __SCHEMA__
|
||||
"""
|
||||
@@ -625,7 +625,7 @@ schema: __SCHEMA__
|
||||
_write(
|
||||
project_dir / "migrations" / "0001_init.sql",
|
||||
sub(
|
||||
"""-- 0001_init: example table + RLS. Applied by `castle program build`
|
||||
"""-- 0001_init: example table + RLS. Applied by `wildpc program build`
|
||||
-- via the versioned migration runner (tracked per-app in
|
||||
-- <schema>.schema_migrations; only unapplied migrations run). Forward-only —
|
||||
-- never edit an applied migration; add a new numbered file instead.
|
||||
@@ -680,7 +680,7 @@ serve((_req: Request) => {
|
||||
sub(
|
||||
"""// Substrate wiring for __NAME__. The anon key is designed to be public
|
||||
// (RLS enforces access) — paste yours here. Find it in the dashboard
|
||||
// Secrets page (or `castle mesh`/vault), secret name SUPABASE_ANON_KEY.
|
||||
// Secrets page (or `wildpc mesh`/vault), secret name SUPABASE_ANON_KEY.
|
||||
window.APP = {
|
||||
SUPABASE_URL: "https://supabase.lan",
|
||||
SUPABASE_ANON_KEY: "PASTE_ANON_KEY_HERE",
|
||||
@@ -748,23 +748,23 @@ window.APP = {
|
||||
__DESC__
|
||||
|
||||
A **supabase-stack** app: it owns its code (this repo) and rents its backend from
|
||||
the shared Supabase substrate (the `supabase` castle service). Only its rows/blobs
|
||||
the shared Supabase substrate (the `supabase` wildpc service). Only its rows/blobs
|
||||
live on the substrate; rebuild the rest from git anytime.
|
||||
|
||||
## Layout
|
||||
- `migrations/` — versioned, idempotent Postgres migrations (forward-only). Applied
|
||||
to the substrate by `castle program build __NAME__`.
|
||||
to the substrate by `wildpc program build __NAME__`.
|
||||
- `functions/` — deno edge functions deployed to the substrate's edge-runtime.
|
||||
- `public/` — static UI served in place at `/__NAME__/` by the gateway. Talks to
|
||||
the substrate with `@supabase/supabase-js` + the public anon key (RLS-gated).
|
||||
- `supabase.app.yaml` — substrate wiring + auth policy (public/private/shared).
|
||||
|
||||
## Develop
|
||||
- Edit `migrations/`, then `castle program build __NAME__` to apply new migrations
|
||||
- Edit `migrations/`, then `wildpc program build __NAME__` to apply new migrations
|
||||
(re-running is a no-op — only unapplied migrations run).
|
||||
- Set the anon key in `public/config.js` (find `SUPABASE_ANON_KEY` in the
|
||||
dashboard Secrets page).
|
||||
- `castle apply` → served at `__NAME__.<domain>`.
|
||||
- `wildpc apply` → served at `__NAME__.<domain>`.
|
||||
|
||||
## Privacy note
|
||||
RLS protects rows, not the static shell or Storage. For a `private`/`shared` app,
|
||||
@@ -799,9 +799,9 @@ def _scaffold_hugo(project_dir: Path, name: str, description: str) -> None:
|
||||
|
||||
The canonical skeleton comes from Hugo's own `hugo new site`; on top of it we
|
||||
overlay the pieces a bare skeleton lacks — the layouts needed to render a home
|
||||
page without a theme, sample content, and a castle-flavored hugo.toml/README.
|
||||
page without a theme, sample content, and a wildpc-flavored hugo.toml/README.
|
||||
|
||||
`castle program build` runs `hugo --gc --minify` → `public/`, which a caddy
|
||||
`wildpc program build` runs `hugo --gc --minify` → `public/`, which a caddy
|
||||
deployment serves in place at <name>.<gateway.domain>. baseURL is `/` so assets
|
||||
resolve at the root of the site's own subdomain. A theme (with its own asset
|
||||
pipeline) can be dropped under themes/ later; such themes declare a two-step
|
||||
@@ -912,7 +912,7 @@ description = "__DESC__"
|
||||
title: "__NAME__"
|
||||
---
|
||||
|
||||
Welcome to **__NAME__** — a Hugo site on castle. Edit `content/_index.md` and
|
||||
Welcome to **__NAME__** — a Hugo site on wildpc. Edit `content/_index.md` and
|
||||
add posts under `content/posts/`.
|
||||
"""
|
||||
),
|
||||
@@ -942,7 +942,7 @@ Your first post. Edit me in `content/posts/`, or run `hugo new posts/next.md`.
|
||||
|
||||
__DESC__
|
||||
|
||||
A [Hugo](https://gohugo.io) static site managed by castle.
|
||||
A [Hugo](https://gohugo.io) static site managed by wildpc.
|
||||
|
||||
## Develop
|
||||
|
||||
@@ -950,8 +950,8 @@ A [Hugo](https://gohugo.io) static site managed by castle.
|
||||
|
||||
## Build & serve
|
||||
|
||||
castle program build __NAME__ # hugo --gc --minify -> public/
|
||||
castle apply __NAME__ # serve at __NAME__.<gateway.domain>
|
||||
wildpc program build __NAME__ # hugo --gc --minify -> public/
|
||||
wildpc apply __NAME__ # serve at __NAME__.<gateway.domain>
|
||||
|
||||
## Themes
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Shared fixtures for castle CLI tests."""
|
||||
"""Shared fixtures for wildpc CLI tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -51,11 +51,11 @@ def _store_for(spec: dict) -> str:
|
||||
]
|
||||
|
||||
|
||||
def _write_castle_config(root: Path, config: dict) -> None:
|
||||
"""Scatter a nested castle config dict into the on-disk layout: castle.yaml globals,
|
||||
def _write_wildpc_config(root: Path, config: dict) -> None:
|
||||
"""Scatter a nested wildpc config dict into the on-disk layout: wildpc.yaml globals,
|
||||
programs/<name>.yaml, and deployments/<kind>/<name>.yaml (fields modernized)."""
|
||||
globals_data = {k: v for k, v in config.items() if k in ("gateway", "repo")}
|
||||
(root / "castle.yaml").write_text(yaml.dump(globals_data, default_flow_style=False))
|
||||
(root / "wildpc.yaml").write_text(yaml.dump(globals_data, default_flow_style=False))
|
||||
programs = config.get("programs") or {}
|
||||
if programs:
|
||||
(root / "programs").mkdir(parents=True, exist_ok=True)
|
||||
@@ -70,8 +70,8 @@ def _write_castle_config(root: Path, config: dict) -> None:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def castle_root(tmp_path: Path) -> Generator[Path, None, None]:
|
||||
"""Create a temporary castle root with directory-per-resource config."""
|
||||
def wildpc_root(tmp_path: Path) -> Generator[Path, None, None]:
|
||||
"""Create a temporary wildpc root with directory-per-resource config."""
|
||||
config = {
|
||||
"gateway": {"port": 18000},
|
||||
"programs": {
|
||||
@@ -129,7 +129,7 @@ def castle_root(tmp_path: Path) -> Generator[Path, None, None]:
|
||||
},
|
||||
},
|
||||
}
|
||||
_write_castle_config(tmp_path, config)
|
||||
_write_wildpc_config(tmp_path, config)
|
||||
|
||||
# Create project directories
|
||||
svc_dir = tmp_path / "test-svc"
|
||||
@@ -143,9 +143,9 @@ def castle_root(tmp_path: Path) -> Generator[Path, None, None]:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def castle_home(tmp_path: Path) -> Generator[Path, None, None]:
|
||||
"""Create a temporary ~/.castle directory."""
|
||||
home = tmp_path / ".castle"
|
||||
def wildpc_home(tmp_path: Path) -> Generator[Path, None, None]:
|
||||
"""Create a temporary ~/.wildpc directory."""
|
||||
home = tmp_path / ".wildpc"
|
||||
home.mkdir()
|
||||
(home / "generated").mkdir()
|
||||
(home / "secrets").mkdir()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for castle add — adopting existing repos as programs."""
|
||||
"""Tests for wildpc add — adopting existing repos as programs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -6,17 +6,17 @@ from argparse import Namespace
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
def _run_add(castle_root: Path, **kwargs: object) -> object:
|
||||
def _run_add(wildpc_root: Path, **kwargs: object) -> object:
|
||||
with (
|
||||
patch("castle_cli.commands.add.load_config") as mock_load,
|
||||
patch("castle_cli.commands.add.save_config"),
|
||||
patch("wildpc_cli.commands.add.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.add.save_config"),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
mock_load.return_value = config
|
||||
from castle_cli.commands.add import run_add
|
||||
from wildpc_cli.commands.add import run_add
|
||||
|
||||
args = Namespace(name=None, description="", **kwargs)
|
||||
rc = run_add(args)
|
||||
@@ -24,49 +24,49 @@ def _run_add(castle_root: Path, **kwargs: object) -> object:
|
||||
|
||||
|
||||
class TestAdd:
|
||||
def test_adopt_python_path_assigns_stack(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_adopt_python_path_assigns_stack(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
repo = tmp_path / "mytool"
|
||||
repo.mkdir()
|
||||
(repo / "pyproject.toml").write_text('[project]\nname = "mytool"\ndependencies = []\n')
|
||||
rc, config = _run_add(castle_root, target=str(repo))
|
||||
rc, config = _run_add(wildpc_root, target=str(repo))
|
||||
assert rc == 0
|
||||
prog = config.programs["mytool"]
|
||||
assert prog.stack == "python-cli" # detected
|
||||
assert prog.source == str(repo.resolve())
|
||||
|
||||
def test_adopt_fastapi_detects_daemon(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_adopt_fastapi_detects_daemon(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
repo = tmp_path / "svc"
|
||||
repo.mkdir()
|
||||
(repo / "pyproject.toml").write_text(
|
||||
'[project]\nname = "svc"\ndependencies = ["fastapi>=0.1"]\n'
|
||||
)
|
||||
rc, config = _run_add(castle_root, target=str(repo))
|
||||
rc, config = _run_add(wildpc_root, target=str(repo))
|
||||
assert rc == 0
|
||||
# `add` adopts source only — no deployment yet (kind is a deployment
|
||||
# property); a fastapi project is detected as the python-fastapi stack.
|
||||
assert config.programs["svc"].stack == "python-fastapi"
|
||||
assert config.deployments_of("svc") == []
|
||||
|
||||
def test_adopt_rust_declares_commands(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_adopt_rust_declares_commands(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
repo = tmp_path / "rusty"
|
||||
repo.mkdir()
|
||||
(repo / "Cargo.toml").write_text('[package]\nname = "rusty"\n')
|
||||
rc, config = _run_add(castle_root, target=str(repo))
|
||||
rc, config = _run_add(wildpc_root, target=str(repo))
|
||||
assert rc == 0
|
||||
prog = config.programs["rusty"]
|
||||
assert prog.stack is None # no castle stack for rust
|
||||
assert prog.stack is None # no wildpc stack for rust
|
||||
# build lands in BuildSpec; other verbs in CommandsSpec
|
||||
assert prog.build is not None
|
||||
assert prog.build.commands == [["cargo", "build", "--release"]]
|
||||
assert prog.commands is not None
|
||||
assert prog.commands.run == [["cargo", "run"]]
|
||||
|
||||
def test_adopt_git_url_records_repo(self, castle_root: Path) -> None:
|
||||
rc, config = _run_add(castle_root, target="https://github.com/someone/widget.git")
|
||||
def test_adopt_git_url_records_repo(self, wildpc_root: Path) -> None:
|
||||
rc, config = _run_add(wildpc_root, target="https://github.com/someone/widget.git")
|
||||
assert rc == 0
|
||||
prog = config.programs["widget"]
|
||||
assert prog.repo == "https://github.com/someone/widget.git"
|
||||
|
||||
def test_missing_path_fails(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
rc, _ = _run_add(castle_root, target=str(tmp_path / "nope"))
|
||||
def test_missing_path_fails(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
rc, _ = _run_add(wildpc_root, target=str(tmp_path / "nope"))
|
||||
assert rc == 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for castle create command."""
|
||||
"""Tests for wildpc create command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -6,24 +6,24 @@ from argparse import Namespace
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
class TestCreateCommand:
|
||||
"""Tests for the create command."""
|
||||
|
||||
def test_create_service(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_create_service(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
"""Create a new service project."""
|
||||
repos = tmp_path / "repos"
|
||||
with (
|
||||
patch("castle_cli.commands.create.load_config") as mock_load,
|
||||
patch("castle_cli.commands.create.save_config") as mock_save,
|
||||
patch("wildpc_cli.commands.create.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.create.save_config") as mock_save,
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
config.repos_dir = repos
|
||||
mock_load.return_value = config
|
||||
|
||||
from castle_cli.commands.create import run_create
|
||||
from wildpc_cli.commands.create import run_create
|
||||
|
||||
args = Namespace(
|
||||
name="my-api",
|
||||
@@ -51,18 +51,18 @@ class TestCreateCommand:
|
||||
assert svc.program == "my-api"
|
||||
mock_save.assert_called_once()
|
||||
|
||||
def test_create_tool(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_create_tool(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
"""Create a new tool project."""
|
||||
repos = tmp_path / "repos"
|
||||
with (
|
||||
patch("castle_cli.commands.create.load_config") as mock_load,
|
||||
patch("castle_cli.commands.create.save_config"),
|
||||
patch("wildpc_cli.commands.create.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.create.save_config"),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
config.repos_dir = repos
|
||||
mock_load.return_value = config
|
||||
|
||||
from castle_cli.commands.create import run_create
|
||||
from wildpc_cli.commands.create import run_create
|
||||
|
||||
args = Namespace(name="my-tool2", stack="python-cli", description="My tool", port=None)
|
||||
result = run_create(args)
|
||||
@@ -77,19 +77,19 @@ class TestCreateCommand:
|
||||
assert config.tools["my-tool2"].manager == "path"
|
||||
assert config.deployments_of("my-tool2") == [("my-tool2", "tool")]
|
||||
|
||||
def test_create_supabase_app(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_create_supabase_app(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
"""A supabase app scaffolds a Patch-shaped project registered as a static
|
||||
frontend (build.outputs=[public]) with no service."""
|
||||
repos = tmp_path / "repos"
|
||||
with (
|
||||
patch("castle_cli.commands.create.load_config") as mock_load,
|
||||
patch("castle_cli.commands.create.save_config"),
|
||||
patch("wildpc_cli.commands.create.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.create.save_config"),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
config.repos_dir = repos
|
||||
mock_load.return_value = config
|
||||
|
||||
from castle_cli.commands.create import run_create
|
||||
from wildpc_cli.commands.create import run_create
|
||||
|
||||
args = Namespace(name="guestbook", stack="supabase", description="Guestbook", port=None)
|
||||
result = run_create(args)
|
||||
@@ -114,13 +114,13 @@ class TestCreateCommand:
|
||||
assert [(r.kind, r.ref) for r in dep.requires] == [("deployment", "supabase")]
|
||||
assert config.deployments_of("guestbook") == [("guestbook", "static")]
|
||||
|
||||
def test_create_duplicate_fails(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_create_duplicate_fails(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""Creating a project with existing name fails."""
|
||||
with patch("castle_cli.commands.create.load_config") as mock_load:
|
||||
config = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.create.load_config") as mock_load:
|
||||
config = load_config(wildpc_root)
|
||||
mock_load.return_value = config
|
||||
|
||||
from castle_cli.commands.create import run_create
|
||||
from wildpc_cli.commands.create import run_create
|
||||
|
||||
# test-svc exists in the services section
|
||||
args = Namespace(
|
||||
@@ -133,17 +133,17 @@ class TestCreateCommand:
|
||||
|
||||
assert result == 1
|
||||
|
||||
def test_create_auto_port(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_create_auto_port(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
"""Service creation auto-assigns next available port."""
|
||||
with (
|
||||
patch("castle_cli.commands.create.load_config") as mock_load,
|
||||
patch("castle_cli.commands.create.save_config"),
|
||||
patch("wildpc_cli.commands.create.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.create.save_config"),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
config.repos_dir = tmp_path / "repos"
|
||||
mock_load.return_value = config
|
||||
|
||||
from castle_cli.commands.create import run_create
|
||||
from wildpc_cli.commands.create import run_create
|
||||
|
||||
args = Namespace(
|
||||
name="auto-port-svc",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for castle delete."""
|
||||
"""Tests for wildpc delete."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -6,17 +6,17 @@ from argparse import Namespace
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
|
||||
def _run_delete(castle_root: Path, **kwargs: object) -> object:
|
||||
def _run_delete(wildpc_root: Path, **kwargs: object) -> object:
|
||||
with (
|
||||
patch("castle_cli.commands.delete.load_config") as mock_load,
|
||||
patch("castle_cli.commands.delete.save_config"),
|
||||
patch("wildpc_cli.commands.delete.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.delete.save_config"),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
mock_load.return_value = config
|
||||
from castle_cli.commands.delete import run_delete
|
||||
from wildpc_cli.commands.delete import run_delete
|
||||
|
||||
args = Namespace(source=False, yes=True, **kwargs)
|
||||
rc = run_delete(args)
|
||||
@@ -24,43 +24,43 @@ def _run_delete(castle_root: Path, **kwargs: object) -> object:
|
||||
|
||||
|
||||
class TestDelete:
|
||||
def test_delete_program(self, castle_root: Path) -> None:
|
||||
rc, config = _run_delete(castle_root, name="test-tool")
|
||||
def test_delete_program(self, wildpc_root: Path) -> None:
|
||||
rc, config = _run_delete(wildpc_root, name="test-tool")
|
||||
assert rc == 0
|
||||
assert "test-tool" not in config.programs
|
||||
|
||||
def test_delete_unknown_fails(self, castle_root: Path) -> None:
|
||||
rc, _ = _run_delete(castle_root, name="does-not-exist")
|
||||
def test_delete_unknown_fails(self, wildpc_root: Path) -> None:
|
||||
rc, _ = _run_delete(wildpc_root, name="does-not-exist")
|
||||
assert rc == 1
|
||||
|
||||
def test_delete_source_removes_dir(self, castle_root: Path, tmp_path: Path) -> None:
|
||||
def test_delete_source_removes_dir(self, wildpc_root: Path, tmp_path: Path) -> None:
|
||||
# Point a program's source at a real temp dir, then delete with --source.
|
||||
src = tmp_path / "victim"
|
||||
src.mkdir()
|
||||
(src / "file.txt").write_text("x")
|
||||
with (
|
||||
patch("castle_cli.commands.delete.load_config") as mock_load,
|
||||
patch("castle_cli.commands.delete.save_config"),
|
||||
patch("wildpc_cli.commands.delete.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.delete.save_config"),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
config.programs["test-tool"].source = str(src)
|
||||
mock_load.return_value = config
|
||||
from castle_cli.commands.delete import run_delete
|
||||
from wildpc_cli.commands.delete import run_delete
|
||||
|
||||
rc = run_delete(Namespace(name="test-tool", source=True, yes=True))
|
||||
assert rc == 0
|
||||
assert not src.exists()
|
||||
|
||||
def test_abort_without_yes_and_no_input(self, castle_root: Path) -> None:
|
||||
def test_abort_without_yes_and_no_input(self, wildpc_root: Path) -> None:
|
||||
# No --yes and no stdin → aborts safely (returns 1, leaves entry).
|
||||
with (
|
||||
patch("castle_cli.commands.delete.load_config") as mock_load,
|
||||
patch("castle_cli.commands.delete.save_config"),
|
||||
patch("wildpc_cli.commands.delete.load_config") as mock_load,
|
||||
patch("wildpc_cli.commands.delete.save_config"),
|
||||
patch("builtins.input", side_effect=EOFError),
|
||||
):
|
||||
config = load_config(castle_root)
|
||||
config = load_config(wildpc_root)
|
||||
mock_load.return_value = config
|
||||
from castle_cli.commands.delete import run_delete
|
||||
from wildpc_cli.commands.delete import run_delete
|
||||
|
||||
rc = run_delete(Namespace(name="test-tool", source=False, yes=False))
|
||||
assert rc == 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for castle doctor."""
|
||||
"""Tests for wildpc doctor."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -7,7 +7,7 @@ from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from castle_cli.commands.doctor import (
|
||||
from wildpc_cli.commands.doctor import (
|
||||
FAIL,
|
||||
OK,
|
||||
WARN,
|
||||
@@ -20,13 +20,13 @@ from castle_cli.commands.doctor import (
|
||||
class TestDoctor:
|
||||
"""The diagnosis path — a bare, unconfigured node should fail loudly."""
|
||||
|
||||
def test_bare_node_reports_problems(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_bare_node_reports_problems(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""No repo:, no control plane, nothing running → exit 1 with fix hints."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
# The shared fixture has no repo: and no castle-gateway/api/dashboard, so the
|
||||
# The shared fixture has no repo: and no wildpc-gateway/api/dashboard, so the
|
||||
# Configuration and Runtime sections must FAIL. Patch where doctor imports it.
|
||||
with patch("castle_core.config.load_config", return_value=load_config(castle_root)):
|
||||
with patch("wildpc_core.config.load_config", return_value=load_config(wildpc_root)):
|
||||
result = run_doctor(Namespace())
|
||||
|
||||
assert result == 1
|
||||
@@ -34,11 +34,11 @@ class TestDoctor:
|
||||
assert "repo: not set" in out
|
||||
assert "control plane missing" in out
|
||||
# Every failing check offers a concrete next command.
|
||||
assert "castle apply" in out
|
||||
assert "wildpc apply" in out
|
||||
|
||||
def test_load_failure_is_first_fail(self, capsys: object) -> None:
|
||||
"""A castle.yaml that won't load is surfaced as a FAIL, not a traceback."""
|
||||
with patch("castle_core.config.load_config", side_effect=ValueError("bad yaml")):
|
||||
"""A wildpc.yaml that won't load is surfaced as a FAIL, not a traceback."""
|
||||
with patch("wildpc_core.config.load_config", side_effect=ValueError("bad yaml")):
|
||||
result = run_doctor(Namespace())
|
||||
|
||||
assert result == 1
|
||||
@@ -48,31 +48,31 @@ class TestDoctor:
|
||||
|
||||
|
||||
class TestDataDirChecks:
|
||||
"""The drift-prevention checks: data_dir must be writable, and a CASTLE_DATA_DIR env
|
||||
"""The drift-prevention checks: data_dir must be writable, and a WILDPC_DATA_DIR env
|
||||
override (the one way the CLI and api can still diverge) must be surfaced."""
|
||||
|
||||
def _config(self, castle_root: Path):
|
||||
from castle_cli.config import load_config
|
||||
def _config(self, wildpc_root: Path):
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
return load_config(castle_root)
|
||||
return load_config(wildpc_root)
|
||||
|
||||
def test_writable_dir_ok_no_warn(
|
||||
self, castle_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
self, wildpc_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.delenv("CASTLE_DATA_DIR", raising=False)
|
||||
monkeypatch.delenv("CASTLE_REPOS_DIR", raising=False)
|
||||
cfg = self._config(castle_root)
|
||||
monkeypatch.delenv("WILDPC_DATA_DIR", raising=False)
|
||||
monkeypatch.delenv("WILDPC_REPOS_DIR", raising=False)
|
||||
cfg = self._config(wildpc_root)
|
||||
cfg.data_dir = tmp_path # exists + writable
|
||||
checks = _check_configuration(cfg)
|
||||
by_label = {c.label: c for c in checks}
|
||||
assert by_label["data dir writable"].status == OK
|
||||
assert not any("overrides castle.yaml" in c.label for c in checks)
|
||||
assert not any("overrides wildpc.yaml" in c.label for c in checks)
|
||||
|
||||
def test_missing_dir_fails_with_hint(
|
||||
self, castle_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
self, wildpc_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.delenv("CASTLE_DATA_DIR", raising=False)
|
||||
cfg = self._config(castle_root)
|
||||
monkeypatch.delenv("WILDPC_DATA_DIR", raising=False)
|
||||
cfg = self._config(wildpc_root)
|
||||
missing = tmp_path / "nope"
|
||||
cfg.data_dir = missing
|
||||
fail = next(
|
||||
@@ -82,16 +82,16 @@ class TestDataDirChecks:
|
||||
assert fail.hint # offers a concrete fix
|
||||
|
||||
def test_env_override_warns(
|
||||
self, castle_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
self, wildpc_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""A CASTLE_DATA_DIR env var overrides the single-source-of-truth file — the
|
||||
"""A WILDPC_DATA_DIR env var overrides the single-source-of-truth file — the
|
||||
exact CLI/api divergence we fixed. Doctor must WARN."""
|
||||
monkeypatch.setenv("CASTLE_DATA_DIR", str(tmp_path))
|
||||
cfg = self._config(castle_root)
|
||||
monkeypatch.setenv("WILDPC_DATA_DIR", str(tmp_path))
|
||||
cfg = self._config(wildpc_root)
|
||||
cfg.data_dir = tmp_path
|
||||
warn = next(c for c in _check_configuration(cfg) if "overrides castle.yaml" in c.label)
|
||||
warn = next(c for c in _check_configuration(cfg) if "overrides wildpc.yaml" in c.label)
|
||||
assert warn.status == WARN
|
||||
assert "CASTLE_DATA_DIR" in warn.detail
|
||||
assert "WILDPC_DATA_DIR" in warn.detail
|
||||
|
||||
|
||||
class TestStackChecks:
|
||||
@@ -99,8 +99,8 @@ class TestStackChecks:
|
||||
run. Missing tooling for an enabled deployment is a FAIL with a copyable fix."""
|
||||
|
||||
def _fastapi_cfg(self):
|
||||
import castle_core.config as C
|
||||
from castle_core.manifest import ProgramSpec, SystemdDeployment
|
||||
import wildpc_core.config as C
|
||||
from wildpc_core.manifest import ProgramSpec, SystemdDeployment
|
||||
|
||||
prog = ProgramSpec(id="svc", stack="python-fastapi")
|
||||
dep = SystemdDeployment.model_validate(
|
||||
@@ -110,7 +110,7 @@ class TestStackChecks:
|
||||
"run": {"launcher": "command", "argv": ["svc"]},
|
||||
}
|
||||
)
|
||||
return C.CastleConfig(
|
||||
return C.WildpcConfig(
|
||||
root=None,
|
||||
gateway=C.GatewayConfig(port=9000),
|
||||
repo=None,
|
||||
@@ -119,7 +119,7 @@ class TestStackChecks:
|
||||
)
|
||||
|
||||
def test_present_tool_is_ok(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
import castle_core.stack_status as SS
|
||||
import wildpc_core.stack_status as SS
|
||||
|
||||
monkeypatch.setattr(SS, "_tool_available", lambda dep, tool: True)
|
||||
fa = next(
|
||||
@@ -130,7 +130,7 @@ class TestStackChecks:
|
||||
def test_missing_tool_for_enabled_deployment_fails_with_hint(
|
||||
self, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
import castle_core.stack_status as SS
|
||||
import wildpc_core.stack_status as SS
|
||||
|
||||
monkeypatch.setattr(SS, "_tool_available", lambda dep, tool: False)
|
||||
fa = next(
|
||||
@@ -141,7 +141,7 @@ class TestStackChecks:
|
||||
|
||||
def test_unused_stacks_are_skipped(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""No react-vite program → no pnpm nag."""
|
||||
import castle_core.stack_status as SS
|
||||
import wildpc_core.stack_status as SS
|
||||
|
||||
monkeypatch.setattr(SS, "_tool_available", lambda dep, tool: True)
|
||||
labels = [c.label for c in _check_stacks(self._fastapi_cfg())]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for castle info command."""
|
||||
"""Tests for wildpc info command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -11,14 +11,14 @@ from unittest.mock import patch
|
||||
class TestInfoCommand:
|
||||
"""Tests for the info command."""
|
||||
|
||||
def test_info_service(self, castle_root: Path, capsys) -> None:
|
||||
def test_info_service(self, wildpc_root: Path, capsys) -> None:
|
||||
"""Show info for a service."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
with patch("castle_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
result = run_info(Namespace(name="test-svc", json=False))
|
||||
|
||||
@@ -28,14 +28,14 @@ class TestInfoCommand:
|
||||
assert "service" in output
|
||||
assert "19000" in output
|
||||
|
||||
def test_info_tool(self, castle_root: Path, capsys) -> None:
|
||||
def test_info_tool(self, wildpc_root: Path, capsys) -> None:
|
||||
"""Show info for a tool."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
with patch("castle_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
result = run_info(Namespace(name="test-tool", json=False))
|
||||
|
||||
@@ -44,14 +44,14 @@ class TestInfoCommand:
|
||||
assert "test-tool" in output
|
||||
assert "tool" in output
|
||||
|
||||
def test_info_not_found(self, castle_root: Path, capsys) -> None:
|
||||
def test_info_not_found(self, wildpc_root: Path, capsys) -> None:
|
||||
"""Info for nonexistent component returns error."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
with patch("castle_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
result = run_info(Namespace(name="nope", json=False))
|
||||
|
||||
@@ -59,14 +59,14 @@ class TestInfoCommand:
|
||||
output = capsys.readouterr().out
|
||||
assert "nope" in output
|
||||
|
||||
def test_info_json_service(self, castle_root: Path, capsys) -> None:
|
||||
def test_info_json_service(self, wildpc_root: Path, capsys) -> None:
|
||||
"""--json produces valid JSON with service fields."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
with patch("castle_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
result = run_info(Namespace(name="test-svc", json=True))
|
||||
|
||||
@@ -76,14 +76,14 @@ class TestInfoCommand:
|
||||
assert data["kind"] == "service"
|
||||
assert data["service"]["expose"]["http"]["internal"]["port"] == 19000
|
||||
|
||||
def test_info_shows_env(self, castle_root: Path, capsys) -> None:
|
||||
def test_info_shows_env(self, wildpc_root: Path, capsys) -> None:
|
||||
"""Info displays environment variables."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
with patch("castle_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
result = run_info(Namespace(name="test-svc", json=False))
|
||||
|
||||
@@ -91,14 +91,14 @@ class TestInfoCommand:
|
||||
output = capsys.readouterr().out
|
||||
assert "TEST_SVC_DATA_DIR" in output
|
||||
|
||||
def test_info_shows_kind(self, castle_root: Path, capsys) -> None:
|
||||
def test_info_shows_kind(self, wildpc_root: Path, capsys) -> None:
|
||||
"""Info displays the derived kind."""
|
||||
from castle_cli.config import load_config
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
with patch("castle_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
with patch("wildpc_cli.commands.info.load_config") as mock_load:
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
|
||||
from castle_cli.commands.info import run_info
|
||||
from wildpc_cli.commands.info import run_info
|
||||
|
||||
result = run_info(Namespace(name="test-svc", json=False))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for castle list command."""
|
||||
"""Tests for wildpc list command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -7,18 +7,18 @@ from argparse import Namespace
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from castle_cli.commands.list_cmd import run_list
|
||||
from wildpc_cli.commands.list_cmd import run_list
|
||||
|
||||
|
||||
class TestListCommand:
|
||||
"""Tests for the list command."""
|
||||
|
||||
def test_list_all(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_list_all(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""List all components."""
|
||||
with patch("castle_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from castle_cli.config import load_config
|
||||
with patch("wildpc_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
args = Namespace(kind=None, stack=None, json=False)
|
||||
result = run_list(args)
|
||||
|
||||
@@ -27,12 +27,12 @@ class TestListCommand:
|
||||
assert "test-svc" in captured.out
|
||||
assert "test-tool" in captured.out
|
||||
|
||||
def test_list_filter_daemon(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_list_filter_daemon(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""--behavior filters the program catalog by its real behavior field."""
|
||||
with patch("castle_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from castle_cli.config import load_config
|
||||
with patch("wildpc_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
args = Namespace(kind="service", stack=None, json=False)
|
||||
result = run_list(args)
|
||||
|
||||
@@ -43,12 +43,12 @@ class TestListCommand:
|
||||
# Services/jobs are deployment views, not behaviors — hidden under a filter.
|
||||
assert "test-svc" not in captured.out
|
||||
|
||||
def test_list_filter_tool(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_list_filter_tool(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""List filtered to tools."""
|
||||
with patch("castle_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from castle_cli.config import load_config
|
||||
with patch("wildpc_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
args = Namespace(kind="tool", stack=None, json=False)
|
||||
result = run_list(args)
|
||||
|
||||
@@ -57,12 +57,12 @@ class TestListCommand:
|
||||
assert "test-tool" in captured.out
|
||||
assert "test-svc" not in captured.out
|
||||
|
||||
def test_list_jobs_are_deployments(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_list_jobs_are_deployments(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""Jobs are a deployment view: shown unfiltered, hidden under a behavior filter."""
|
||||
with patch("castle_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from castle_cli.config import load_config
|
||||
with patch("wildpc_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
# Unfiltered: the job appears.
|
||||
run_list(Namespace(kind=None, stack=None, json=False))
|
||||
assert "test-job" in capsys.readouterr().out # type: ignore[attr-defined]
|
||||
@@ -73,12 +73,12 @@ class TestListCommand:
|
||||
assert "test-svc" not in out
|
||||
assert "test-tool" in out
|
||||
|
||||
def test_list_json(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_list_json(self, wildpc_root: Path, capsys: object) -> None:
|
||||
"""JSON output tags each entry with its derived kind."""
|
||||
with patch("castle_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from castle_cli.config import load_config
|
||||
with patch("wildpc_cli.commands.list_cmd.load_config") as mock_load:
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
mock_load.return_value = load_config(castle_root)
|
||||
mock_load.return_value = load_config(wildpc_root)
|
||||
args = Namespace(kind=None, stack=None, json=True)
|
||||
result = run_list(args)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for the castle mesh command (API calls mocked)."""
|
||||
"""Tests for the wildpc mesh command (API calls mocked)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -6,7 +6,7 @@ import urllib.error
|
||||
from argparse import Namespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from castle_cli.commands.mesh import run_mesh
|
||||
from wildpc_cli.commands.mesh import run_mesh
|
||||
|
||||
|
||||
class TestMeshCommand:
|
||||
@@ -15,7 +15,7 @@ class TestMeshCommand:
|
||||
"enabled": True, "connected": True, "nats_url": "tls://x:4222",
|
||||
"peer_count": 1, "peers": ["primer"],
|
||||
}
|
||||
with patch("castle_cli.commands.mesh._get", return_value=data):
|
||||
with patch("wildpc_cli.commands.mesh._get", return_value=data):
|
||||
rc = run_mesh(Namespace(mesh_command="status"))
|
||||
assert rc == 0
|
||||
out = capsys.readouterr().out # type: ignore[attr-defined]
|
||||
@@ -28,7 +28,7 @@ class TestMeshCommand:
|
||||
{"hostname": "primer", "online": True, "is_stale": False,
|
||||
"deployed_count": 3, "is_local": False},
|
||||
]
|
||||
with patch("castle_cli.commands.mesh._get", return_value=data):
|
||||
with patch("wildpc_cli.commands.mesh._get", return_value=data):
|
||||
rc = run_mesh(Namespace(mesh_command="nodes"))
|
||||
assert rc == 0
|
||||
out = capsys.readouterr().out # type: ignore[attr-defined]
|
||||
@@ -36,7 +36,7 @@ class TestMeshCommand:
|
||||
|
||||
def test_config_list(self, capsys: object) -> None:
|
||||
data = {"role": "authority", "keys": ["fleet/motd"]}
|
||||
with patch("castle_cli.commands.mesh._get", return_value=data):
|
||||
with patch("wildpc_cli.commands.mesh._get", return_value=data):
|
||||
rc = run_mesh(
|
||||
Namespace(mesh_command="config", mesh_config_command="list")
|
||||
)
|
||||
@@ -45,7 +45,7 @@ class TestMeshCommand:
|
||||
|
||||
def test_config_set_calls_put(self) -> None:
|
||||
with patch(
|
||||
"castle_cli.commands.mesh._put", return_value={"ok": True}
|
||||
"wildpc_cli.commands.mesh._put", return_value={"ok": True}
|
||||
) as put:
|
||||
rc = run_mesh(
|
||||
Namespace(
|
||||
@@ -60,7 +60,7 @@ class TestMeshCommand:
|
||||
|
||||
def test_api_unreachable_returns_1(self, capsys: object) -> None:
|
||||
with patch(
|
||||
"castle_cli.commands.mesh._get",
|
||||
"wildpc_cli.commands.mesh._get",
|
||||
side_effect=urllib.error.URLError("refused"),
|
||||
):
|
||||
rc = run_mesh(Namespace(mesh_command="status"))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for the `castle secret` command (reads/writes the active backend)."""
|
||||
"""Tests for the `wildpc secret` command (reads/writes the active backend)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -13,13 +13,13 @@ def file_secrets(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
||||
"""Force the active backend to a temp file store."""
|
||||
secrets = tmp_path / "secrets"
|
||||
secrets.mkdir()
|
||||
monkeypatch.setenv("CASTLE_SECRET_BACKEND", "file")
|
||||
monkeypatch.setattr("castle_core.config.SECRETS_DIR", secrets)
|
||||
monkeypatch.setenv("WILDPC_SECRET_BACKEND", "file")
|
||||
monkeypatch.setattr("wildpc_core.config.SECRETS_DIR", secrets)
|
||||
return secrets
|
||||
|
||||
|
||||
def _run(**kw: object) -> int:
|
||||
from castle_cli.commands.secret import run_secret
|
||||
from wildpc_cli.commands.secret import run_secret
|
||||
|
||||
return run_secret(Namespace(**kw))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for the `castle tool` lens."""
|
||||
"""Tests for the `wildpc tool` lens."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -8,16 +8,16 @@ from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
def _config(castle_root: Path):
|
||||
from castle_cli.config import load_config
|
||||
def _config(wildpc_root: Path):
|
||||
from wildpc_cli.config import load_config
|
||||
|
||||
return load_config(castle_root)
|
||||
return load_config(wildpc_root)
|
||||
|
||||
|
||||
class TestToolList:
|
||||
def test_list_includes_tool(self, castle_root: Path, capsys: object) -> None:
|
||||
with patch("castle_cli.commands.tool.load_config", return_value=_config(castle_root)):
|
||||
from castle_cli.commands.tool import run_tool_list
|
||||
def test_list_includes_tool(self, wildpc_root: Path, capsys: object) -> None:
|
||||
with patch("wildpc_cli.commands.tool.load_config", return_value=_config(wildpc_root)):
|
||||
from wildpc_cli.commands.tool import run_tool_list
|
||||
|
||||
rc = run_tool_list(Namespace(json=False))
|
||||
assert rc == 0
|
||||
@@ -26,9 +26,9 @@ class TestToolList:
|
||||
# test-svc is a service (systemd), not a tool — must not appear.
|
||||
assert "test-svc" not in out
|
||||
|
||||
def test_list_json_payload(self, castle_root: Path, capsys: object) -> None:
|
||||
with patch("castle_cli.commands.tool.load_config", return_value=_config(castle_root)):
|
||||
from castle_cli.commands.tool import run_tool_list
|
||||
def test_list_json_payload(self, wildpc_root: Path, capsys: object) -> None:
|
||||
with patch("wildpc_cli.commands.tool.load_config", return_value=_config(wildpc_root)):
|
||||
from wildpc_cli.commands.tool import run_tool_list
|
||||
|
||||
rc = run_tool_list(Namespace(json=True))
|
||||
assert rc == 0
|
||||
@@ -41,9 +41,9 @@ class TestToolList:
|
||||
|
||||
|
||||
class TestToolInfo:
|
||||
def test_info_json(self, castle_root: Path, capsys: object) -> None:
|
||||
with patch("castle_cli.commands.tool.load_config", return_value=_config(castle_root)):
|
||||
from castle_cli.commands.tool import run_tool_info
|
||||
def test_info_json(self, wildpc_root: Path, capsys: object) -> None:
|
||||
with patch("wildpc_cli.commands.tool.load_config", return_value=_config(wildpc_root)):
|
||||
from wildpc_cli.commands.tool import run_tool_info
|
||||
|
||||
rc = run_tool_info(Namespace(name="test-tool", json=True))
|
||||
assert rc == 0
|
||||
@@ -51,10 +51,10 @@ class TestToolInfo:
|
||||
assert data["name"] == "test-tool"
|
||||
assert data["executables"]
|
||||
|
||||
def test_info_rejects_non_tool(self, castle_root: Path, capsys: object) -> None:
|
||||
def test_info_rejects_non_tool(self, wildpc_root: Path, capsys: object) -> None:
|
||||
# test-svc is a service deployment, not a tool.
|
||||
with patch("castle_cli.commands.tool.load_config", return_value=_config(castle_root)):
|
||||
from castle_cli.commands.tool import run_tool_info
|
||||
with patch("wildpc_cli.commands.tool.load_config", return_value=_config(wildpc_root)):
|
||||
from wildpc_cli.commands.tool import run_tool_info
|
||||
|
||||
rc = run_tool_info(Namespace(name="test-svc", json=False))
|
||||
assert rc == 1
|
||||
|
||||
425
cli/uv.lock
generated
425
cli/uv.lock
generated
@@ -1,425 +0,0 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[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 = "castle-cli"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "jinja2" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "jinja2", specifier = ">=3.1.0" },
|
||||
{ name = "pydantic", specifier = ">=2.0.0" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "pyright", specifier = ">=1.1.0" },
|
||||
{ name = "pytest", specifier = ">=7.0.0" },
|
||||
{ name = "ruff", specifier = ">=0.11.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
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 = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markupsafe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "26.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.5"
|
||||
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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.41.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.408"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodeenv" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/04/eab13a954e763b0606f460443fcbf6bb5a0faf06890ea3754ff16523dce5/ruff-0.15.2.tar.gz", hash = "sha256:14b965afee0969e68bb871eba625343b8673375f457af4abe98553e8bbb98342", size = 4558148, upload-time = "2026-02-19T22:32:20.271Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/70/3a4dc6d09b13cb3e695f28307e5d889b2e1a66b7af9c5e257e796695b0e6/ruff-0.15.2-py3-none-linux_armv6l.whl", hash = "sha256:120691a6fdae2f16d65435648160f5b81a9625288f75544dc40637436b5d3c0d", size = 10430565, upload-time = "2026-02-19T22:32:41.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/0b/bb8457b56185ece1305c666dc895832946d24055be90692381c31d57466d/ruff-0.15.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a89056d831256099658b6bba4037ac6dd06f49d194199215befe2bb10457ea5e", size = 10820354, upload-time = "2026-02-19T22:32:07.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/c1/e0532d7f9c9e0b14c46f61b14afd563298b8b83f337b6789ddd987e46121/ruff-0.15.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e36dee3a64be0ebd23c86ffa3aa3fd3ac9a712ff295e192243f814a830b6bd87", size = 10170767, upload-time = "2026-02-19T22:32:13.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/e8/da1aa341d3af017a21c7a62fb5ec31d4e7ad0a93ab80e3a508316efbcb23/ruff-0.15.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9fb47b6d9764677f8c0a193c0943ce9a05d6763523f132325af8a858eadc2b9", size = 10529591, upload-time = "2026-02-19T22:32:02.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/74/184fbf38e9f3510231fbc5e437e808f0b48c42d1df9434b208821efcd8d6/ruff-0.15.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f376990f9d0d6442ea9014b19621d8f2aaf2b8e39fdbfc79220b7f0c596c9b80", size = 10260771, upload-time = "2026-02-19T22:32:36.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/ac/605c20b8e059a0bc4b42360414baa4892ff278cec1c91fff4be0dceedefd/ruff-0.15.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dcc987551952d73cbf5c88d9fdee815618d497e4df86cd4c4824cc59d5dd75f", size = 11045791, upload-time = "2026-02-19T22:32:31.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/52/db6e419908f45a894924d410ac77d64bdd98ff86901d833364251bd08e22/ruff-0.15.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42a47fd785cbe8c01b9ff45031af875d101b040ad8f4de7bbb716487c74c9a77", size = 11879271, upload-time = "2026-02-19T22:32:29.305Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/d8/7992b18f2008bdc9231d0f10b16df7dda964dbf639e2b8b4c1b4e91b83af/ruff-0.15.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe9f49354866e575b4c6943856989f966421870e85cd2ac94dccb0a9dcb2fea", size = 11303707, upload-time = "2026-02-19T22:32:22.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/02/849b46184bcfdd4b64cde61752cc9a146c54759ed036edd11857e9b8443b/ruff-0.15.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7a672c82b5f9887576087d97be5ce439f04bbaf548ee987b92d3a7dede41d3a", size = 11149151, upload-time = "2026-02-19T22:32:44.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/04/f5284e388bab60d1d3b99614a5a9aeb03e0f333847e2429bebd2aaa1feec/ruff-0.15.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ecc64f46f7019e2bcc3cdc05d4a7da958b629a5ab7033195e11a438403d956", size = 11091132, upload-time = "2026-02-19T22:32:24.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/ae/88d844a21110e14d92cf73d57363fab59b727ebeabe78009b9ccb23500af/ruff-0.15.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8dcf243b15b561c655c1ef2f2b0050e5d50db37fe90115507f6ff37d865dc8b4", size = 10504717, upload-time = "2026-02-19T22:32:26.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/27/867076a6ada7f2b9c8292884ab44d08fd2ba71bd2b5364d4136f3cd537e1/ruff-0.15.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dab6941c862c05739774677c6273166d2510d254dac0695c0e3f5efa1b5585de", size = 10263122, upload-time = "2026-02-19T22:32:10.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/ef/faf9321d550f8ebf0c6373696e70d1758e20ccdc3951ad7af00c0956be7c/ruff-0.15.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b9164f57fc36058e9a6806eb92af185b0697c9fe4c7c52caa431c6554521e5c", size = 10735295, upload-time = "2026-02-19T22:32:39.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/55/e8089fec62e050ba84d71b70e7834b97709ca9b7aba10c1a0b196e493f97/ruff-0.15.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:80d24fcae24d42659db7e335b9e1531697a7102c19185b8dc4a028b952865fd8", size = 11241641, upload-time = "2026-02-19T22:32:34.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/01/1c30526460f4d23222d0fabd5888868262fd0e2b71a00570ca26483cd993/ruff-0.15.2-py3-none-win32.whl", hash = "sha256:fd5ff9e5f519a7e1bd99cbe8daa324010a74f5e2ebc97c6242c08f26f3714f6f", size = 10507885, upload-time = "2026-02-19T22:32:15.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/10/3d18e3bbdf8fc50bbb4ac3cc45970aa5a9753c5cb51bf9ed9a3cd8b79fa3/ruff-0.15.2-py3-none-win_amd64.whl", hash = "sha256:d20014e3dfa400f3ff84830dfb5755ece2de45ab62ecea4af6b7262d0fb4f7c5", size = 11623725, upload-time = "2026-02-19T22:32:04.947Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
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" },
|
||||
]
|
||||
Reference in New Issue
Block a user