- db.py: migrations + DAOs for tokens / projects / jobs / findings (SQLite WAL)
- auth.py: SHA-256 bearer hashing + LAN-CIDR allowlist + admin/app token tiers
- models.py: Pydantic shapes (Project, Subproject, Schedule, Notify, Job, CreateJobRequest)
- server.py: FastAPI on port 8810; /healthz, /admin/tokens/*, /projects/*, /jobs, /jobs/{id}, /jobs/{id}/log, /jobs/{id}/findings
- runner.py: bounded asyncio pool, per-job timeout with process-group SIGTERM→SIGKILL escalation, orphaned-job recovery on boot
- workspace.py: bare-clone + worktree materialization, gc
- config.py: env-driven
- 62 tests across db / auth / projects / jobs / runner / e2e — all green
Cross-token project access returns 404 (not 403) — existence-leak guard.
Bearer tokens hashed at rest; admin token bootstrapped on first boot.
Recipe subprocess uses start_new_session=True so killpg targets the
whole process tree on timeout — child processes can't escape SIGKILL.
Pump task guarded with wait_for(2s) + cancel fallback against any
orphan that survives the group kill.
Wave 2 (parsers + findings extraction + MCP + email digest) pending.
Spec: memory/spec-crafting-table.md
40 lines
1.1 KiB
TOML
40 lines
1.1 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=68", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "crafting-table"
|
|
version = "0.1.0"
|
|
description = "Polyglot dev/build/audit container — HTTP API + async job runner."
|
|
readme = "README.md"
|
|
license = { text = "MIT" }
|
|
requires-python = ">=3.11"
|
|
authors = [
|
|
{ name = "Kayos", email = "kayos@sulkta.com" },
|
|
]
|
|
dependencies = [
|
|
"fastapi>=0.115,<1.0",
|
|
"uvicorn[standard]>=0.30,<1.0",
|
|
"pydantic>=2.7,<3.0",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
test = [
|
|
"pytest>=8.0",
|
|
"pytest-asyncio>=0.23",
|
|
"httpx>=0.27",
|
|
]
|
|
|
|
[tool.setuptools.packages.find]
|
|
include = ["crafting_table*"]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
filterwarnings = [
|
|
# asyncio's BaseSubprocessTransport.__del__ can fire after the test loop
|
|
# closes when subprocess cleanup races with pytest-asyncio's loop teardown.
|
|
# The test logic verifies the subprocess actually died; the late __del__
|
|
# touch is harmless. Suppressing keeps pytest's AST cache from blowing up.
|
|
"ignore::pytest.PytestUnraisableExceptionWarning",
|
|
]
|